activerecord-oracle_enhanced-adapter 8.1.0-java

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 (78) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +1971 -0
  3. data/License.txt +20 -0
  4. data/README.md +947 -0
  5. data/VERSION +1 -0
  6. data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +7 -0
  7. data/lib/active_record/connection_adapters/oracle_enhanced/column.rb +24 -0
  8. data/lib/active_record/connection_adapters/oracle_enhanced/connection.rb +137 -0
  9. data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +359 -0
  10. data/lib/active_record/connection_adapters/oracle_enhanced/database_limits.rb +47 -0
  11. data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +325 -0
  12. data/lib/active_record/connection_adapters/oracle_enhanced/database_tasks.rb +63 -0
  13. data/lib/active_record/connection_adapters/oracle_enhanced/dbms_output.rb +71 -0
  14. data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb +629 -0
  15. data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_quoting.rb +38 -0
  16. data/lib/active_record/connection_adapters/oracle_enhanced/lob.rb +57 -0
  17. data/lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb +465 -0
  18. data/lib/active_record/connection_adapters/oracle_enhanced/oci_quoting.rb +44 -0
  19. data/lib/active_record/connection_adapters/oracle_enhanced/procedures.rb +195 -0
  20. data/lib/active_record/connection_adapters/oracle_enhanced/quoting.rb +186 -0
  21. data/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb +95 -0
  22. data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +99 -0
  23. data/lib/active_record/connection_adapters/oracle_enhanced/schema_dumper.rb +197 -0
  24. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +739 -0
  25. data/lib/active_record/connection_adapters/oracle_enhanced/structure_dump.rb +394 -0
  26. data/lib/active_record/connection_adapters/oracle_enhanced/type_metadata.rb +34 -0
  27. data/lib/active_record/connection_adapters/oracle_enhanced/version.rb +3 -0
  28. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +886 -0
  29. data/lib/active_record/type/oracle_enhanced/boolean.rb +19 -0
  30. data/lib/active_record/type/oracle_enhanced/character_string.rb +36 -0
  31. data/lib/active_record/type/oracle_enhanced/integer.rb +14 -0
  32. data/lib/active_record/type/oracle_enhanced/json.rb +10 -0
  33. data/lib/active_record/type/oracle_enhanced/national_character_string.rb +26 -0
  34. data/lib/active_record/type/oracle_enhanced/national_character_text.rb +36 -0
  35. data/lib/active_record/type/oracle_enhanced/raw.rb +25 -0
  36. data/lib/active_record/type/oracle_enhanced/string.rb +29 -0
  37. data/lib/active_record/type/oracle_enhanced/text.rb +32 -0
  38. data/lib/active_record/type/oracle_enhanced/timestampltz.rb +25 -0
  39. data/lib/active_record/type/oracle_enhanced/timestamptz.rb +25 -0
  40. data/lib/activerecord-oracle_enhanced-adapter.rb +25 -0
  41. data/lib/arel/visitors/oracle.rb +216 -0
  42. data/lib/arel/visitors/oracle12.rb +121 -0
  43. data/lib/arel/visitors/oracle_common.rb +51 -0
  44. data/spec/active_record/connection_adapters/emulation/oracle_adapter_spec.rb +24 -0
  45. data/spec/active_record/connection_adapters/oracle_enhanced/compatibility_spec.rb +40 -0
  46. data/spec/active_record/connection_adapters/oracle_enhanced/composite_spec.rb +84 -0
  47. data/spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb +589 -0
  48. data/spec/active_record/connection_adapters/oracle_enhanced/context_index_spec.rb +431 -0
  49. data/spec/active_record/connection_adapters/oracle_enhanced/database_tasks_spec.rb +122 -0
  50. data/spec/active_record/connection_adapters/oracle_enhanced/dbconsole_spec.rb +63 -0
  51. data/spec/active_record/connection_adapters/oracle_enhanced/dbms_output_spec.rb +69 -0
  52. data/spec/active_record/connection_adapters/oracle_enhanced/procedures_spec.rb +362 -0
  53. data/spec/active_record/connection_adapters/oracle_enhanced/quoting_spec.rb +181 -0
  54. data/spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb +492 -0
  55. data/spec/active_record/connection_adapters/oracle_enhanced/schema_statements_spec.rb +1318 -0
  56. data/spec/active_record/connection_adapters/oracle_enhanced/structure_dump_spec.rb +485 -0
  57. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +815 -0
  58. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +230 -0
  59. data/spec/active_record/oracle_enhanced/type/binary_spec.rb +119 -0
  60. data/spec/active_record/oracle_enhanced/type/boolean_spec.rb +206 -0
  61. data/spec/active_record/oracle_enhanced/type/character_string_spec.rb +67 -0
  62. data/spec/active_record/oracle_enhanced/type/custom_spec.rb +90 -0
  63. data/spec/active_record/oracle_enhanced/type/decimal_spec.rb +56 -0
  64. data/spec/active_record/oracle_enhanced/type/dirty_spec.rb +141 -0
  65. data/spec/active_record/oracle_enhanced/type/float_spec.rb +48 -0
  66. data/spec/active_record/oracle_enhanced/type/integer_spec.rb +101 -0
  67. data/spec/active_record/oracle_enhanced/type/json_spec.rb +56 -0
  68. data/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb +55 -0
  69. data/spec/active_record/oracle_enhanced/type/national_character_text_spec.rb +230 -0
  70. data/spec/active_record/oracle_enhanced/type/raw_spec.rb +137 -0
  71. data/spec/active_record/oracle_enhanced/type/text_spec.rb +295 -0
  72. data/spec/active_record/oracle_enhanced/type/timestamp_spec.rb +107 -0
  73. data/spec/spec_config.yaml.template +11 -0
  74. data/spec/spec_helper.rb +225 -0
  75. data/spec/support/alter_system_set_open_cursors.sql +1 -0
  76. data/spec/support/alter_system_user_password.sql +2 -0
  77. data/spec/support/create_oracle_enhanced_users.sql +31 -0
  78. metadata +181 -0
@@ -0,0 +1,394 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord # :nodoc:
4
+ module ConnectionAdapters # :nodoc:
5
+ module OracleEnhanced # :nodoc:
6
+ module StructureDump # :nodoc:
7
+ # Statements separator used in structure dump to allow loading of structure dump also with SQL*Plus
8
+ STATEMENT_TOKEN = "\n\n/\n\n"
9
+
10
+ def structure_dump # :nodoc:
11
+ sequences = select(<<~SQL.squish, "SCHEMA")
12
+ SELECT
13
+ sequence_name, min_value, max_value, increment_by, order_flag, cycle_flag
14
+ FROM all_sequences
15
+ where sequence_owner = SYS_CONTEXT('userenv', 'current_schema') ORDER BY 1
16
+ SQL
17
+
18
+ structure = sequences.map do |result|
19
+ "CREATE SEQUENCE #{quote_table_name(result["sequence_name"])} MINVALUE #{result["min_value"]} MAXVALUE #{result["max_value"]} INCREMENT BY #{result["increment_by"]} #{result["order_flag"] == 'Y' ? "ORDER" : "NOORDER"} #{result["cycle_flag"] == 'Y' ? "CYCLE" : "NOCYCLE"}"
20
+ end
21
+ tables = select_values(<<~SQL.squish, "SCHEMA")
22
+ SELECT table_name FROM all_tables t
23
+ WHERE owner = SYS_CONTEXT('userenv', 'current_schema') AND secondary = 'N'
24
+ AND NOT EXISTS (SELECT mv.mview_name FROM all_mviews mv
25
+ WHERE mv.owner = t.owner AND mv.mview_name = t.table_name)
26
+ AND NOT EXISTS (SELECT mvl.log_table FROM all_mview_logs mvl
27
+ WHERE mvl.log_owner = t.owner AND mvl.log_table = t.table_name)
28
+ ORDER BY 1
29
+ SQL
30
+ tables.each do |table_name|
31
+ virtual_columns = virtual_columns_for(table_name) if supports_virtual_columns?
32
+ ddl = +"CREATE#{ ' GLOBAL TEMPORARY' if temporary_table?(table_name)} TABLE \"#{table_name}\" (\n"
33
+ columns = select_all(<<~SQL.squish, "SCHEMA", [bind_string("table_name", table_name)])
34
+ SELECT column_name, data_type, data_length, char_used, char_length,
35
+ data_precision, data_scale, data_default, nullable
36
+ FROM all_tab_columns
37
+ WHERE table_name = :table_name
38
+ AND owner = SYS_CONTEXT('userenv', 'current_schema')
39
+ ORDER BY column_id
40
+ SQL
41
+ cols = columns.map do |row|
42
+ if (v = virtual_columns.find { |col| col["column_name"] == row["column_name"] })
43
+ structure_dump_virtual_column(row, v["data_default"])
44
+ else
45
+ structure_dump_column(row)
46
+ end
47
+ end
48
+ ddl << cols.map { |col| " #{col}" }.join(",\n")
49
+ ddl << structure_dump_primary_key(table_name)
50
+ ddl << "\n)"
51
+ structure << ddl
52
+ structure << structure_dump_indexes(table_name)
53
+ structure << structure_dump_unique_keys(table_name)
54
+ structure << structure_dump_check_constraints(table_name)
55
+ structure << structure_dump_table_comments(table_name)
56
+ structure << structure_dump_column_comments(table_name)
57
+ end
58
+
59
+ join_with_statement_token(structure) <<
60
+ structure_dump_fk_constraints <<
61
+ structure_dump_views
62
+ end
63
+
64
+ def structure_dump_column(column) # :nodoc:
65
+ col = +"\"#{column['column_name']}\" #{column['data_type']}"
66
+ if (column["data_type"] == "NUMBER") && !column["data_precision"].nil?
67
+ col << "(#{column['data_precision'].to_i}"
68
+ col << ",#{column['data_scale'].to_i}" if !column["data_scale"].nil?
69
+ col << ")"
70
+ elsif column["data_type"].include?("CHAR") || column["data_type"] == "RAW"
71
+ length = column["char_used"] == "C" ? column["char_length"].to_i : column["data_length"].to_i
72
+ col << "(#{length})"
73
+ end
74
+ col << " DEFAULT #{column['data_default']}" if !column["data_default"].nil?
75
+ col << " NOT NULL" if column["nullable"] == "N"
76
+ col
77
+ end
78
+
79
+ def structure_dump_virtual_column(column, data_default) # :nodoc:
80
+ data_default = data_default.delete('"')
81
+ col = +"\"#{column['column_name']}\" #{column['data_type']}"
82
+ if (column["data_type"] == "NUMBER") && !column["data_precision"].nil?
83
+ col << "(#{column['data_precision'].to_i}"
84
+ col << ",#{column['data_scale'].to_i}" if !column["data_scale"].nil?
85
+ col << ")"
86
+ elsif column["data_type"].include?("CHAR") || column["data_type"] == "RAW"
87
+ length = column["char_used"] == "C" ? column["char_length"].to_i : column["data_length"].to_i
88
+ col << "(#{length})"
89
+ end
90
+ col << " GENERATED ALWAYS AS (#{data_default}) VIRTUAL"
91
+ end
92
+
93
+ def structure_dump_primary_key(table) # :nodoc:
94
+ opts = { name: "", cols: [] }
95
+ pks = select_all(<<~SQL.squish, "SCHEMA")
96
+ SELECT a.constraint_name, a.column_name, a.position
97
+ FROM all_cons_columns a
98
+ JOIN all_constraints c
99
+ ON a.constraint_name = c.constraint_name
100
+ WHERE c.table_name = '#{table.upcase}'
101
+ AND c.constraint_type = 'P'
102
+ AND a.owner = c.owner
103
+ AND c.owner = SYS_CONTEXT('userenv', 'current_schema')
104
+ SQL
105
+ pks.each do |row|
106
+ opts[:name] = row["constraint_name"]
107
+ opts[:cols][row["position"] - 1] = row["column_name"]
108
+ end
109
+ opts[:cols].length > 0 ? ",\n CONSTRAINT #{opts[:name]} PRIMARY KEY (#{opts[:cols].join(',')})" : ""
110
+ end
111
+
112
+ def structure_dump_unique_keys(table) # :nodoc:
113
+ keys = {}
114
+ uks = select_all(<<~SQL.squish, "SCHEMA")
115
+ SELECT a.constraint_name, a.column_name, a.position
116
+ FROM all_cons_columns a
117
+ JOIN all_constraints c
118
+ ON a.constraint_name = c.constraint_name
119
+ WHERE c.table_name = '#{table.upcase}'
120
+ AND c.constraint_type = 'U'
121
+ AND a.owner = c.owner
122
+ AND c.owner = SYS_CONTEXT('userenv', 'current_schema')
123
+ SQL
124
+ uks.each do |uk|
125
+ keys[uk["constraint_name"]] ||= []
126
+ keys[uk["constraint_name"]][uk["position"] - 1] = uk["column_name"]
127
+ end
128
+ keys.map do |k, v|
129
+ "ALTER TABLE #{table.upcase} ADD CONSTRAINT #{k} UNIQUE (#{v.join(',')})"
130
+ end
131
+ end
132
+
133
+ def structure_dump_indexes(table_name) # :nodoc:
134
+ indexes(table_name).map do |options|
135
+ column_names = options.columns
136
+ options = { name: options.name, unique: options.unique }
137
+ index_name = index_name(table_name, column: column_names)
138
+ if Hash === options # legacy support, since this param was a string
139
+ index_type = options[:unique] ? "UNIQUE" : ""
140
+ index_name = options[:name] || index_name
141
+ else
142
+ index_type = options
143
+ end
144
+ quoted_column_names = column_names.map { |e| quote_column_name_or_expression(e) }.join(", ")
145
+ "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})"
146
+ end
147
+ end
148
+
149
+ def structure_dump_fk_constraints # :nodoc:
150
+ foreign_keys = select_all(<<~SQL.squish, "SCHEMA")
151
+ SELECT table_name FROM all_tables
152
+ WHERE owner = SYS_CONTEXT('userenv', 'current_schema') ORDER BY 1
153
+ SQL
154
+ fks = foreign_keys.map do |table|
155
+ if respond_to?(:foreign_keys) && (foreign_keys = foreign_keys(table["table_name"])).any?
156
+ foreign_keys.map do |fk|
157
+ sql = +"ALTER TABLE #{quote_table_name(fk.from_table)} ADD CONSTRAINT #{quote_column_name(fk.options[:name])} "
158
+ sql << "#{foreign_key_definition(fk.to_table, fk.options)}"
159
+ end
160
+ end
161
+ end.flatten.compact
162
+ join_with_statement_token(fks)
163
+ end
164
+
165
+ # Only user-named CHECK constraints are preserved. Anonymous
166
+ # constraints created via `ALTER TABLE ... ADD CHECK (...)` receive
167
+ # Oracle-generated names (e.g. SYS_C00123) and are skipped to avoid
168
+ # also emitting the implicit NOT NULL check constraints that Oracle
169
+ # stores with constraint_type = 'C'.
170
+ def structure_dump_check_constraints(table_name) # :nodoc:
171
+ # `search_condition` is a LONG column, so it cannot appear in a
172
+ # WHERE clause (ORA-00997). The driver reads it as a String on
173
+ # SELECT. Implicit NOT NULL constraints Oracle stores with
174
+ # constraint_type = 'C' are excluded by `generated = 'USER NAME'`
175
+ # since they receive system-generated names.
176
+ check_constraints = select_all(<<~SQL.squish, "SCHEMA", [bind_string("table_name", table_name.upcase)])
177
+ SELECT constraint_name, search_condition
178
+ FROM all_constraints
179
+ WHERE owner = SYS_CONTEXT('userenv', 'current_schema')
180
+ AND table_name = :table_name
181
+ AND constraint_type = 'C'
182
+ AND generated = 'USER NAME'
183
+ ORDER BY constraint_name
184
+ SQL
185
+ check_constraints.filter_map do |row|
186
+ condition = row["search_condition"]
187
+ next if condition.nil?
188
+ "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{quote_column_name(row["constraint_name"])} CHECK (#{condition})"
189
+ end
190
+ end
191
+
192
+ def structure_dump_table_comments(table_name)
193
+ comments = []
194
+ comment = table_comment(table_name)
195
+
196
+ unless comment.nil?
197
+ comments << "COMMENT ON TABLE #{quote_table_name(table_name)} IS '#{quote_string(comment)}'"
198
+ end
199
+
200
+ join_with_statement_token(comments)
201
+ end
202
+
203
+ def structure_dump_column_comments(table_name)
204
+ comments = []
205
+ columns = select_values(<<~SQL.squish, "SCHEMA", [bind_string("table_name", table_name)])
206
+ SELECT column_name FROM all_tab_columns
207
+ WHERE owner = SYS_CONTEXT('userenv', 'current_schema')
208
+ AND table_name = :table_name ORDER BY column_id
209
+ SQL
210
+
211
+ columns.each do |column|
212
+ comment = column_comment(table_name, column)
213
+ unless comment.nil?
214
+ comments << "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{quote_column_name(column)} IS '#{quote_string(comment)}'"
215
+ end
216
+ end
217
+
218
+ join_with_statement_token(comments)
219
+ end
220
+
221
+ def foreign_key_definition(to_table, options = {}) # :nodoc:
222
+ column_sql = quote_column_name(options[:column] || "#{to_table.to_s.singularize}_id")
223
+ references = options[:references] ? options[:references].first : nil
224
+ references_sql = quote_column_name(options[:primary_key] || references || "id")
225
+
226
+ sql = "FOREIGN KEY (#{column_sql}) REFERENCES #{quote_table_name(to_table)}(#{references_sql})"
227
+
228
+ case options[:dependent]
229
+ when :nullify
230
+ sql << " ON DELETE SET NULL"
231
+ when :delete
232
+ sql << " ON DELETE CASCADE"
233
+ end
234
+ sql
235
+ end
236
+
237
+ # Extract all stored procedures, packages, synonyms.
238
+ def structure_dump_db_stored_code # :nodoc:
239
+ structure = []
240
+ all_source = select_all(<<~SQL.squish, "SCHEMA")
241
+ SELECT DISTINCT name, type
242
+ FROM all_source
243
+ WHERE type IN ('PROCEDURE', 'PACKAGE', 'PACKAGE BODY', 'FUNCTION', 'TRIGGER', 'TYPE')
244
+ AND name NOT LIKE 'BIN$%'
245
+ AND owner = SYS_CONTEXT('userenv', 'current_schema') ORDER BY type
246
+ SQL
247
+ all_source.each do |source|
248
+ ddl = +"CREATE OR REPLACE \n"
249
+ texts = select_all(<<~SQL.squish, "all source at structure dump", [bind_string("source_name", source["name"]), bind_string("source_type", source["type"])])
250
+ SELECT text
251
+ FROM all_source
252
+ WHERE name = :source_name
253
+ AND type = :source_type
254
+ AND owner = SYS_CONTEXT('userenv', 'current_schema')
255
+ ORDER BY line
256
+ SQL
257
+ texts.each do |row|
258
+ ddl << row["text"]
259
+ end
260
+ ddl << ";" unless ddl.strip[-1, 1] == ";"
261
+ structure << ddl
262
+ end
263
+
264
+ # export synonyms
265
+ structure << structure_dump_synonyms
266
+
267
+ join_with_statement_token(structure)
268
+ end
269
+
270
+ def structure_dump_views # :nodoc:
271
+ structure = []
272
+ views = select_all(<<~SQL.squish, "SCHEMA")
273
+ SELECT view_name, text FROM all_views
274
+ WHERE owner = SYS_CONTEXT('userenv', 'current_schema') ORDER BY view_name ASC
275
+ SQL
276
+ views.each do |view|
277
+ structure << "CREATE OR REPLACE FORCE VIEW #{view['view_name']} AS\n #{view['text']}"
278
+ end
279
+ join_with_statement_token(structure)
280
+ end
281
+
282
+ def structure_dump_synonyms # :nodoc:
283
+ structure = []
284
+ synonyms = select_all(<<~SQL.squish, "SCHEMA")
285
+ SELECT owner, synonym_name, table_name, table_owner
286
+ FROM all_synonyms
287
+ WHERE owner = SYS_CONTEXT('userenv', 'current_schema')
288
+ SQL
289
+ synonyms.each do |synonym|
290
+ structure << "CREATE OR REPLACE #{synonym['owner'] == 'PUBLIC' ? 'PUBLIC' : '' } SYNONYM #{synonym['synonym_name']}
291
+ FOR #{synonym['table_owner']}.#{synonym['table_name']}"
292
+ end
293
+ join_with_statement_token(structure)
294
+ end
295
+
296
+ def structure_drop # :nodoc:
297
+ sequences = select_values(<<~SQL.squish, "SCHEMA")
298
+ SELECT
299
+ sequence_name FROM all_sequences where sequence_owner = SYS_CONTEXT('userenv', 'current_schema') ORDER BY 1
300
+ SQL
301
+ statements = sequences.map do |seq|
302
+ "DROP SEQUENCE \"#{seq}\""
303
+ end
304
+ tables = select_values(<<~SQL.squish, "SCHEMA")
305
+ SELECT table_name from all_tables t
306
+ WHERE owner = SYS_CONTEXT('userenv', 'current_schema') AND secondary = 'N'
307
+ AND NOT EXISTS (SELECT mv.mview_name FROM all_mviews mv
308
+ WHERE mv.owner = t.owner AND mv.mview_name = t.table_name)
309
+ AND NOT EXISTS (SELECT mvl.log_table FROM all_mview_logs mvl
310
+ WHERE mvl.log_owner = t.owner AND mvl.log_table = t.table_name)
311
+ ORDER BY 1
312
+ SQL
313
+ tables.each do |table|
314
+ statements << "DROP TABLE \"#{table}\" CASCADE CONSTRAINTS"
315
+ end
316
+ join_with_statement_token(statements)
317
+ end
318
+
319
+ def temp_table_drop # :nodoc:
320
+ temporary_tables = select_values(<<~SQL.squish, "SCHEMA")
321
+ SELECT table_name FROM all_tables
322
+ WHERE owner = SYS_CONTEXT('userenv', 'current_schema')
323
+ AND secondary = 'N' AND temporary = 'Y' ORDER BY 1
324
+ SQL
325
+ statements = temporary_tables.map do |table|
326
+ "DROP TABLE \"#{table}\" CASCADE CONSTRAINTS"
327
+ end
328
+ join_with_statement_token(statements)
329
+ end
330
+
331
+ def full_drop(preserve_tables = false) # :nodoc:
332
+ s = preserve_tables ? [] : [structure_drop]
333
+ s << temp_table_drop if preserve_tables
334
+ s << drop_sql_for_feature("view")
335
+ s << drop_sql_for_feature("materialized view")
336
+ s << drop_sql_for_feature("synonym")
337
+ s << drop_sql_for_feature("type")
338
+ s << drop_sql_for_object("package")
339
+ s << drop_sql_for_object("function")
340
+ s << drop_sql_for_object("procedure")
341
+ s.join
342
+ end
343
+
344
+ def execute_structure_dump(string)
345
+ string.split(STATEMENT_TOKEN).each do |ddl|
346
+ execute(ddl) unless ddl.blank?
347
+ end
348
+ end
349
+
350
+ private
351
+ # Called only if `supports_virtual_columns?` returns true
352
+ # return [{'column_name' => 'FOOS', 'data_default' => '...'}, ...]
353
+ def virtual_columns_for(table)
354
+ select_all(<<~SQL.squish, "SCHEMA", [bind_string("table_name", table.upcase)])
355
+ SELECT column_name, data_default
356
+ FROM all_tab_cols
357
+ WHERE virtual_column = 'YES'
358
+ AND owner = SYS_CONTEXT('userenv', 'current_schema')
359
+ AND table_name = :table_name
360
+ SQL
361
+ end
362
+
363
+ def drop_sql_for_feature(type)
364
+ short_type = type == "materialized view" ? "mview" : type
365
+ features = select_values(<<~SQL.squish, "SCHEMA")
366
+ SELECT #{short_type}_name FROM all_#{short_type.tableize}
367
+ where owner = SYS_CONTEXT('userenv', 'current_schema')
368
+ SQL
369
+ statements = features.map do |name|
370
+ "DROP #{type.upcase} \"#{name}\""
371
+ end
372
+ join_with_statement_token(statements)
373
+ end
374
+
375
+ def drop_sql_for_object(type)
376
+ objects = select_values(<<~SQL.squish, "SCHEMA")
377
+ SELECT object_name FROM all_objects
378
+ WHERE object_type = '#{type.upcase}' and owner = SYS_CONTEXT('userenv', 'current_schema')
379
+ SQL
380
+ statements = objects.map do |name|
381
+ "DROP #{type.upcase} \"#{name}\""
382
+ end
383
+ join_with_statement_token(statements)
384
+ end
385
+
386
+ def join_with_statement_token(array)
387
+ string = array.join(STATEMENT_TOKEN)
388
+ string << STATEMENT_TOKEN unless string.blank?
389
+ string
390
+ end
391
+ end
392
+ end
393
+ end
394
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters # :nodoc:
5
+ module OracleEnhanced
6
+ class TypeMetadata < DelegateClass(ActiveRecord::ConnectionAdapters::SqlTypeMetadata) # :nodoc:
7
+ include Deduplicable
8
+
9
+ attr_reader :virtual
10
+
11
+ def initialize(type_metadata, virtual: nil)
12
+ super(type_metadata)
13
+ @type_metadata = type_metadata
14
+ @virtual = virtual
15
+ end
16
+
17
+ def ==(other)
18
+ other.is_a?(OracleEnhanced::TypeMetadata) &&
19
+ attributes_for_hash == other.attributes_for_hash
20
+ end
21
+ alias eql? ==
22
+
23
+ def hash
24
+ attributes_for_hash.hash
25
+ end
26
+
27
+ protected
28
+ def attributes_for_hash
29
+ [self.class, @type_metadata, virtual]
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter::VERSION = File.read(File.expand_path("../../../../../VERSION", __FILE__)).chomp