pmacs-activerecord-oracle_enhanced-adapter 1.5.6.1 → 1.6.2.1

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 (51) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +7 -7
  3. data/History.md +126 -0
  4. data/README.md +285 -178
  5. data/Rakefile +1 -1
  6. data/VERSION +1 -1
  7. data/lib/active_record/connection_adapters/{oracle_enhanced_column.rb → oracle_enhanced/column.rb} +14 -63
  8. data/lib/active_record/connection_adapters/oracle_enhanced/column_dumper.rb +65 -0
  9. data/lib/active_record/connection_adapters/{oracle_enhanced_connection.rb → oracle_enhanced/connection.rb} +2 -2
  10. data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +347 -0
  11. data/lib/active_record/connection_adapters/{oracle_enhanced_cpk.rb → oracle_enhanced/cpk.rb} +0 -0
  12. data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +257 -0
  13. data/lib/active_record/connection_adapters/{oracle_enhanced_database_tasks.rb → oracle_enhanced/database_tasks.rb} +0 -0
  14. data/lib/active_record/connection_adapters/oracle_enhanced/dirty.rb +40 -0
  15. data/lib/active_record/connection_adapters/{oracle_enhanced_jdbc_connection.rb → oracle_enhanced/jdbc_connection.rb} +0 -0
  16. data/lib/active_record/connection_adapters/{oracle_enhanced_oci_connection.rb → oracle_enhanced/oci_connection.rb} +0 -0
  17. data/lib/active_record/connection_adapters/{oracle_enhanced_procedures.rb → oracle_enhanced/procedures.rb} +1 -3
  18. data/lib/active_record/connection_adapters/{oracle_enhanced_schema_creation.rb → oracle_enhanced/schema_creation.rb} +34 -35
  19. data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +95 -0
  20. data/lib/active_record/connection_adapters/{oracle_enhanced_schema_dumper.rb → oracle_enhanced/schema_dumper.rb} +4 -32
  21. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +548 -0
  22. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements_ext.rb +74 -0
  23. data/lib/active_record/connection_adapters/{oracle_enhanced_structure_dump.rb → oracle_enhanced/structure_dump.rb} +28 -6
  24. data/lib/active_record/connection_adapters/oracle_enhanced/version.rb +1 -0
  25. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +161 -68
  26. data/lib/active_record/oracle_enhanced/type/integer.rb +13 -0
  27. data/lib/active_record/oracle_enhanced/type/raw.rb +13 -0
  28. data/lib/active_record/oracle_enhanced/type/timestamp.rb +11 -0
  29. data/lib/pmacs-activerecord-oracle_enhanced-adapter.rb +1 -1
  30. data/pmacs-activerecord-oracle_enhanced-adapter.gemspec +35 -31
  31. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +6 -31
  32. data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +1 -1
  33. data/spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb +2 -2
  34. data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +2 -2
  35. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +75 -63
  36. data/spec/active_record/connection_adapters/oracle_enhanced_database_tasks_spec.rb +1 -1
  37. data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +7 -13
  38. data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +2 -1
  39. data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +25 -178
  40. data/spec/active_record/connection_adapters/oracle_enhanced_schema_statements_spec.rb +60 -5
  41. data/spec/active_record/connection_adapters/oracle_enhanced_structure_dump_spec.rb +1 -0
  42. data/spec/spec_helper.rb +21 -10
  43. metadata +32 -28
  44. data/lib/active_record/connection_adapters/oracle_enhanced_column_dumper.rb +0 -77
  45. data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +0 -350
  46. data/lib/active_record/connection_adapters/oracle_enhanced_database_statements.rb +0 -262
  47. data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +0 -45
  48. data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +0 -223
  49. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb +0 -450
  50. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +0 -267
  51. data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +0 -1
@@ -7,6 +7,7 @@ module ActiveRecord #:nodoc:
7
7
  private
8
8
  alias_method_chain :tables, :oracle_enhanced
9
9
  alias_method_chain :indexes, :oracle_enhanced
10
+ alias_method_chain :foreign_keys, :oracle_enhanced
10
11
  end
11
12
  end
12
13
 
@@ -56,37 +57,8 @@ module ActiveRecord #:nodoc:
56
57
  end
57
58
  end
58
59
 
59
- def foreign_keys(table_name, stream)
60
- if @connection.respond_to?(:foreign_keys) && (foreign_keys = @connection.foreign_keys(table_name)).any?
61
- add_foreign_key_statements = foreign_keys.map do |foreign_key|
62
- statement_parts = [ ('add_foreign_key ' + foreign_key.from_table.inspect) ]
63
- statement_parts << foreign_key.to_table.inspect
64
-
65
- if foreign_key.options[:columns].size == 1
66
- column = foreign_key.options[:columns].first
67
- if column != "#{foreign_key.to_table.singularize}_id"
68
- statement_parts << ('column: ' + column.inspect)
69
- end
70
-
71
- if foreign_key.options[:references].first != 'id'
72
- statement_parts << ('primary_key: ' + foreign_key.options[:references].first.inspect)
73
- end
74
- else
75
- statement_parts << ('columns: ' + foreign_key.options[:columns].inspect)
76
- end
77
-
78
- statement_parts << ('name: ' + foreign_key.options[:name].inspect)
79
-
80
- unless foreign_key.options[:dependent].blank?
81
- statement_parts << ('dependent: ' + foreign_key.options[:dependent].inspect)
82
- end
83
-
84
- ' ' + statement_parts.join(', ')
85
- end
86
-
87
- stream.puts add_foreign_key_statements.sort.join("\n")
88
- stream.puts
89
- end
60
+ def foreign_keys_with_oracle_enhanced(table_name, stream)
61
+ return foreign_keys_without_oracle_enhanced(table_name, stream)
90
62
  end
91
63
 
92
64
  def synonyms(stream)
@@ -166,7 +138,7 @@ module ActiveRecord #:nodoc:
166
138
  else
167
139
  tbl.print ", id: false"
168
140
  end
169
- tbl.print ", force: true"
141
+ tbl.print ", force: :cascade"
170
142
  tbl.puts " do |t|"
171
143
 
172
144
  # then dump all non-primary key columns
@@ -0,0 +1,548 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'digest/sha1'
3
+
4
+ module ActiveRecord
5
+ module ConnectionAdapters
6
+ module OracleEnhanced
7
+ module SchemaStatements
8
+ # SCHEMA STATEMENTS ========================================
9
+ #
10
+ # see: abstract/schema_statements.rb
11
+
12
+ # Additional options for +create_table+ method in migration files.
13
+ #
14
+ # You can specify individual starting value in table creation migration file, e.g.:
15
+ #
16
+ # create_table :users, :sequence_start_value => 100 do |t|
17
+ # # ...
18
+ # end
19
+ #
20
+ # You can also specify other sequence definition additional parameters, e.g.:
21
+ #
22
+ # create_table :users, :sequence_start_value => “100 NOCACHE INCREMENT BY 10” do |t|
23
+ # # ...
24
+ # end
25
+ #
26
+ # Create primary key trigger (so that you can skip primary key value in INSERT statement).
27
+ # By default trigger name will be "table_name_pkt", you can override the name with
28
+ # :trigger_name option (but it is not recommended to override it as then this trigger will
29
+ # not be detected by ActiveRecord model and it will still do prefetching of sequence value).
30
+ # Example:
31
+ #
32
+ # create_table :users, :primary_key_trigger => true do |t|
33
+ # # ...
34
+ # end
35
+ #
36
+ # It is possible to add table and column comments in table creation migration files:
37
+ #
38
+ # create_table :employees, :comment => “Employees and contractors” do |t|
39
+ # t.string :first_name, :comment => “Given name”
40
+ # t.string :last_name, :comment => “Surname”
41
+ # end
42
+
43
+ def create_table(table_name, options = {})
44
+ create_sequence = options[:id] != false
45
+ column_comments = {}
46
+ temporary = options.delete(:temporary)
47
+ additional_options = options
48
+ td = create_table_definition table_name, temporary, additional_options
49
+ td.primary_key(options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false
50
+
51
+ # store that primary key was defined in create_table block
52
+ unless create_sequence
53
+ class << td
54
+ attr_accessor :create_sequence
55
+ def primary_key(*args)
56
+ self.create_sequence = true
57
+ super(*args)
58
+ end
59
+ end
60
+ end
61
+
62
+ # store column comments
63
+ class << td
64
+ attr_accessor :column_comments
65
+ def column(name, type, options = {})
66
+ if options[:comment]
67
+ self.column_comments ||= {}
68
+ self.column_comments[name] = options[:comment]
69
+ end
70
+ super(name, type, options)
71
+ end
72
+ end
73
+
74
+ yield td if block_given?
75
+ create_sequence = create_sequence || td.create_sequence
76
+ column_comments = td.column_comments if td.column_comments
77
+ tablespace = tablespace_for(:table, options[:tablespace])
78
+
79
+ if options[:force] && table_exists?(table_name)
80
+ drop_table(table_name, options)
81
+ end
82
+
83
+ execute schema_creation.accept td
84
+
85
+ create_sequence_and_trigger(table_name, options) if create_sequence
86
+
87
+ add_table_comment table_name, options[:comment]
88
+ column_comments.each do |column_name, comment|
89
+ add_comment table_name, column_name, comment
90
+ end
91
+ td.indexes.each_pair { |c,o| add_index table_name, c, o }
92
+
93
+ td.foreign_keys.each_pair do |other_table_name, foreign_key_options|
94
+ add_foreign_key(table_name, other_table_name, foreign_key_options)
95
+ end
96
+ end
97
+
98
+ def create_table_definition(name, temporary, options)
99
+ ActiveRecord::ConnectionAdapters::OracleEnhanced::TableDefinition.new native_database_types, name, temporary, options
100
+ end
101
+
102
+ def rename_table(table_name, new_name) #:nodoc:
103
+ if new_name.to_s.length > table_name_length
104
+ raise ArgumentError, "New table name '#{new_name}' is too long; the limit is #{table_name_length} characters"
105
+ end
106
+ if "#{new_name}_seq".to_s.length > sequence_name_length
107
+ raise ArgumentError, "New sequence name '#{new_name}_seq' is too long; the limit is #{sequence_name_length} characters"
108
+ end
109
+ execute "RENAME #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
110
+ execute "RENAME #{quote_table_name("#{table_name}_seq")} TO #{quote_table_name("#{new_name}_seq")}" rescue nil
111
+
112
+ rename_table_indexes(table_name, new_name)
113
+ end
114
+
115
+ def drop_table(table_name, options = {}) #:nodoc:
116
+ execute "DROP TABLE #{quote_table_name(table_name)}#{' CASCADE CONSTRAINTS' if options[:force] == :cascade}"
117
+ seq_name = options[:sequence_name] || default_sequence_name(table_name)
118
+ execute "DROP SEQUENCE #{quote_table_name(seq_name)}" rescue nil
119
+ rescue ActiveRecord::StatementInvalid => e
120
+ raise e unless options[:if_exists]
121
+ ensure
122
+ clear_table_columns_cache(table_name)
123
+ self.all_schema_indexes = nil
124
+ end
125
+
126
+ def dump_schema_information #:nodoc:
127
+ sm_table = ActiveRecord::Migrator.schema_migrations_table_name
128
+ migrated = select_values("SELECT version FROM #{sm_table} ORDER BY version")
129
+ join_with_statement_token(migrated.map{|v| "INSERT INTO #{sm_table} (version) VALUES ('#{v}')" })
130
+ end
131
+
132
+ def initialize_schema_migrations_table
133
+ sm_table = ActiveRecord::Migrator.schema_migrations_table_name
134
+
135
+ unless table_exists?(sm_table)
136
+ index_name = "#{Base.table_name_prefix}unique_schema_migrations#{Base.table_name_suffix}"
137
+ if index_name.length > index_name_length
138
+ truncate_to = index_name_length - index_name.to_s.length - 1
139
+ truncated_name = "unique_schema_migrations"[0..truncate_to]
140
+ index_name = "#{Base.table_name_prefix}#{truncated_name}#{Base.table_name_suffix}"
141
+ end
142
+
143
+ create_table(sm_table, :id => false) do |schema_migrations_table|
144
+ schema_migrations_table.column :version, :string, :null => false
145
+ end
146
+ add_index sm_table, :version, :unique => true, :name => index_name
147
+
148
+ # Backwards-compatibility: if we find schema_info, assume we've
149
+ # migrated up to that point:
150
+ si_table = Base.table_name_prefix + 'schema_info' + Base.table_name_suffix
151
+ if table_exists?(si_table)
152
+ ActiveSupport::Deprecation.warn "Usage of the schema table `#{si_table}` is deprecated. Please switch to using `schema_migrations` table"
153
+
154
+ old_version = select_value("SELECT version FROM #{quote_table_name(si_table)}").to_i
155
+ assume_migrated_upto_version(old_version)
156
+ drop_table(si_table)
157
+ end
158
+ end
159
+ end
160
+
161
+ def update_table_definition(table_name, base) #:nodoc:
162
+ OracleEnhanced::Table.new(table_name, base)
163
+ end
164
+
165
+ def add_index(table_name, column_name, options = {}) #:nodoc:
166
+ index_name, index_type, quoted_column_names, tablespace, index_options = add_index_options(table_name, column_name, options)
167
+ execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} (#{quoted_column_names})#{tablespace} #{index_options}"
168
+ if index_type == 'UNIQUE'
169
+ unless quoted_column_names =~ /\(.*\)/
170
+ execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{quote_column_name(index_name)} #{index_type} (#{quoted_column_names})"
171
+ end
172
+ end
173
+ ensure
174
+ self.all_schema_indexes = nil
175
+ end
176
+
177
+ def add_index_options(table_name, column_name, options = {}) #:nodoc:
178
+ column_names = Array(column_name)
179
+ index_name = index_name(table_name, column: column_names)
180
+
181
+ options.assert_valid_keys(:unique, :order, :name, :where, :length, :internal, :tablespace, :options, :using)
182
+
183
+ index_type = options[:unique] ? "UNIQUE" : ""
184
+ index_name = options[:name].to_s if options.key?(:name)
185
+ tablespace = tablespace_for(:index, options[:tablespace])
186
+ max_index_length = options.fetch(:internal, false) ? index_name_length : allowed_index_name_length
187
+ #TODO: This option is used for NOLOGGING, needs better argumetn name
188
+ index_options = options[:options]
189
+
190
+ if index_name.to_s.length > max_index_length
191
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{max_index_length} characters"
192
+ end
193
+ if index_name_exists?(table_name, index_name, false)
194
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
195
+ end
196
+
197
+ quoted_column_names = column_names.map { |e| quote_column_name_or_expression(e) }.join(", ")
198
+ [index_name, index_type, quoted_column_names, tablespace, index_options]
199
+ end
200
+
201
+ # Remove the given index from the table.
202
+ # Gives warning if index does not exist
203
+ def remove_index(table_name, options = {}) #:nodoc:
204
+ index_name = index_name(table_name, options)
205
+ unless index_name_exists?(table_name, index_name, true)
206
+ # sometimes options can be String or Array with column names
207
+ options = {} unless options.is_a?(Hash)
208
+ if options.has_key? :name
209
+ options_without_column = options.dup
210
+ options_without_column.delete :column
211
+ index_name_without_column = index_name(table_name, options_without_column)
212
+ return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
213
+ end
214
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
215
+ end
216
+ remove_index!(table_name, index_name)
217
+ end
218
+
219
+ # clear cached indexes when removing index
220
+ def remove_index!(table_name, index_name) #:nodoc:
221
+ #TODO: It should execute only when index_type == "UNIQUE"
222
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(index_name)}" rescue nil
223
+ execute "DROP INDEX #{quote_column_name(index_name)}"
224
+ ensure
225
+ self.all_schema_indexes = nil
226
+ end
227
+
228
+ # returned shortened index name if default is too large
229
+ def index_name(table_name, options) #:nodoc:
230
+ default_name = super(table_name, options).to_s
231
+ # sometimes options can be String or Array with column names
232
+ options = {} unless options.is_a?(Hash)
233
+ identifier_max_length = options[:identifier_max_length] || index_name_length
234
+ return default_name if default_name.length <= identifier_max_length
235
+
236
+ # remove 'index', 'on' and 'and' keywords
237
+ shortened_name = "i_#{table_name}_#{Array(options[:column]) * '_'}"
238
+
239
+ # leave just first three letters from each word
240
+ if shortened_name.length > identifier_max_length
241
+ shortened_name = shortened_name.split('_').map{|w| w[0,3]}.join('_')
242
+ end
243
+ # generate unique name using hash function
244
+ if shortened_name.length > identifier_max_length
245
+ shortened_name = 'i'+Digest::SHA1.hexdigest(default_name)[0,identifier_max_length-1]
246
+ end
247
+ @logger.warn "#{adapter_name} shortened default index name #{default_name} to #{shortened_name}" if @logger
248
+ shortened_name
249
+ end
250
+
251
+ # Verify the existence of an index with a given name.
252
+ #
253
+ # The default argument is returned if the underlying implementation does not define the indexes method,
254
+ # as there's no way to determine the correct answer in that case.
255
+ #
256
+ # Will always query database and not index cache.
257
+ def index_name_exists?(table_name, index_name, default)
258
+ (owner, table_name, db_link) = @connection.describe(table_name)
259
+ result = select_value(<<-SQL)
260
+ SELECT 1 FROM all_indexes#{db_link} i
261
+ WHERE i.owner = '#{owner}'
262
+ AND i.table_owner = '#{owner}'
263
+ AND i.table_name = '#{table_name}'
264
+ AND i.index_name = '#{index_name.to_s.upcase}'
265
+ SQL
266
+ result == 1
267
+ end
268
+
269
+ def rename_index(table_name, old_name, new_name) #:nodoc:
270
+ unless index_name_exists?(table_name, old_name, true)
271
+ raise ArgumentError, "Index name '#{old_name}' on table '#{table_name}' does not exist"
272
+ end
273
+ if new_name.length > allowed_index_name_length
274
+ raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters"
275
+ end
276
+ execute "ALTER INDEX #{quote_column_name(old_name)} rename to #{quote_column_name(new_name)}"
277
+ ensure
278
+ self.all_schema_indexes = nil
279
+ end
280
+
281
+ def add_column(table_name, column_name, type, options = {}) #:nodoc:
282
+ if type.to_sym == :virtual
283
+ type = options[:type]
284
+ end
285
+ type = aliased_types(type.to_s, type)
286
+ add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} "
287
+ add_column_sql << type_to_sql(type, options[:limit], options[:precision], options[:scale]) if type
288
+
289
+ add_column_options!(add_column_sql, options.merge(:type=>type, :column_name=>column_name, :table_name=>table_name))
290
+
291
+ add_column_sql << tablespace_for((type_to_sql(type).downcase.to_sym), nil, table_name, column_name) if type
292
+
293
+ execute(add_column_sql)
294
+
295
+ create_sequence_and_trigger(table_name, options) if type && type.to_sym == :primary_key
296
+ ensure
297
+ clear_table_columns_cache(table_name)
298
+ end
299
+
300
+ def aliased_types(name, fallback)
301
+ fallback
302
+ end
303
+
304
+ def change_column_default(table_name, column_name, default) #:nodoc:
305
+ execute "ALTER TABLE #{quote_table_name(table_name)} MODIFY #{quote_column_name(column_name)} DEFAULT #{quote(default)}"
306
+ ensure
307
+ clear_table_columns_cache(table_name)
308
+ end
309
+
310
+ def change_column_null(table_name, column_name, null, default = nil) #:nodoc:
311
+ column = column_for(table_name, column_name)
312
+
313
+ unless null || default.nil?
314
+ execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
315
+ end
316
+
317
+ change_column table_name, column_name, column.sql_type, :null => null
318
+ end
319
+
320
+ def change_column(table_name, column_name, type, options = {}) #:nodoc:
321
+ column = column_for(table_name, column_name)
322
+
323
+ # remove :null option if its value is the same as current column definition
324
+ # otherwise Oracle will raise error
325
+ if options.has_key?(:null) && options[:null] == column.null
326
+ options[:null] = nil
327
+ end
328
+ if type.to_sym == :virtual
329
+ type = options[:type]
330
+ end
331
+ change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} MODIFY #{quote_column_name(column_name)} "
332
+ change_column_sql << "#{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" if type
333
+
334
+ add_column_options!(change_column_sql, options.merge(:type=>type, :column_name=>column_name, :table_name=>table_name))
335
+
336
+ change_column_sql << tablespace_for((type_to_sql(type).downcase.to_sym), nil, options[:table_name], options[:column_name]) if type
337
+
338
+ execute(change_column_sql)
339
+ ensure
340
+ clear_table_columns_cache(table_name)
341
+ end
342
+
343
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
344
+ execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} to #{quote_column_name(new_column_name)}"
345
+ self.all_schema_indexes = nil
346
+ rename_column_indexes(table_name, column_name, new_column_name)
347
+ ensure
348
+ clear_table_columns_cache(table_name)
349
+ end
350
+
351
+ def remove_column(table_name, column_name, type = nil, options = {}) #:nodoc:
352
+ execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)} CASCADE CONSTRAINTS"
353
+ ensure
354
+ clear_table_columns_cache(table_name)
355
+ self.all_schema_indexes = nil
356
+ end
357
+
358
+ def add_comment(table_name, column_name, comment) #:nodoc:
359
+ return if comment.blank?
360
+ execute "COMMENT ON COLUMN #{quote_table_name(table_name)}.#{column_name} IS '#{comment}'"
361
+ end
362
+
363
+ def add_table_comment(table_name, comment) #:nodoc:
364
+ return if comment.blank?
365
+ execute "COMMENT ON TABLE #{quote_table_name(table_name)} IS '#{comment}'"
366
+ end
367
+
368
+ def table_comment(table_name) #:nodoc:
369
+ (owner, table_name, db_link) = @connection.describe(table_name)
370
+ select_value <<-SQL
371
+ SELECT comments FROM all_tab_comments#{db_link}
372
+ WHERE owner = '#{owner}'
373
+ AND table_name = '#{table_name}'
374
+ SQL
375
+ end
376
+
377
+ def column_comment(table_name, column_name) #:nodoc:
378
+ (owner, table_name, db_link) = @connection.describe(table_name)
379
+ select_value <<-SQL
380
+ SELECT comments FROM all_col_comments#{db_link}
381
+ WHERE owner = '#{owner}'
382
+ AND table_name = '#{table_name}'
383
+ AND column_name = '#{column_name.upcase}'
384
+ SQL
385
+ end
386
+
387
+ # Maps logical Rails types to Oracle-specific data types.
388
+ def type_to_sql(type, limit = nil, precision = nil, scale = nil) #:nodoc:
389
+ # Ignore options for :text and :binary columns
390
+ return super(type, nil, nil, nil) if ['text', 'binary'].include?(type.to_s)
391
+
392
+ super
393
+ end
394
+
395
+ def tablespace(table_name)
396
+ select_value <<-SQL
397
+ SELECT tablespace_name
398
+ FROM user_tables
399
+ WHERE table_name='#{table_name.to_s.upcase}'
400
+ SQL
401
+ end
402
+
403
+ def add_foreign_key(from_table, to_table, options = {})
404
+ if options[:dependent]
405
+ ActiveSupport::Deprecation.warn "`:dependent` option will be deprecated. Please use `:on_delete` option"
406
+ end
407
+ case options[:dependent]
408
+ when :delete then options[:on_delete] = :cascade
409
+ when :nullify then options[:on_delete] = :nullify
410
+ else
411
+ end
412
+
413
+ super
414
+ end
415
+
416
+ def remove_foreign_key(from_table, options_or_to_table = {})
417
+ super
418
+ end
419
+
420
+ # get table foreign keys for schema dump
421
+ def foreign_keys(table_name) #:nodoc:
422
+ (owner, desc_table_name, db_link) = @connection.describe(table_name)
423
+
424
+ fk_info = select_all(<<-SQL, 'Foreign Keys')
425
+ SELECT r.table_name to_table
426
+ ,rc.column_name references_column
427
+ ,cc.column_name
428
+ ,c.constraint_name name
429
+ ,c.delete_rule
430
+ FROM user_constraints#{db_link} c, user_cons_columns#{db_link} cc,
431
+ user_constraints#{db_link} r, user_cons_columns#{db_link} rc
432
+ WHERE c.owner = '#{owner}'
433
+ AND c.table_name = '#{desc_table_name}'
434
+ AND c.constraint_type = 'R'
435
+ AND cc.owner = c.owner
436
+ AND cc.constraint_name = c.constraint_name
437
+ AND r.constraint_name = c.r_constraint_name
438
+ AND r.owner = c.owner
439
+ AND rc.owner = r.owner
440
+ AND rc.constraint_name = r.constraint_name
441
+ AND rc.position = cc.position
442
+ ORDER BY name, to_table, column_name, references_column
443
+ SQL
444
+
445
+ fk_info.map do |row|
446
+ options = {
447
+ column: oracle_downcase(row['column_name']),
448
+ name: oracle_downcase(row['name']),
449
+ primary_key: oracle_downcase(row['references_column'])
450
+ }
451
+ options[:on_delete] = extract_foreign_key_action(row['delete_rule'])
452
+ OracleEnhanced::ForeignKeyDefinition.new(oracle_downcase(table_name), oracle_downcase(row['to_table']), options)
453
+ end
454
+ end
455
+
456
+ def extract_foreign_key_action(specifier) # :nodoc:
457
+ case specifier
458
+ when 'CASCADE'; :cascade
459
+ when 'SET NULL'; :nullify
460
+ end
461
+ end
462
+
463
+ # REFERENTIAL INTEGRITY ====================================
464
+
465
+ def disable_referential_integrity(&block) #:nodoc:
466
+ sql_constraints = <<-SQL
467
+ SELECT constraint_name, owner, table_name
468
+ FROM user_constraints
469
+ WHERE constraint_type = 'R'
470
+ AND status = 'ENABLED'
471
+ SQL
472
+ old_constraints = select_all(sql_constraints)
473
+ begin
474
+ old_constraints.each do |constraint|
475
+ execute "ALTER TABLE #{constraint["table_name"]} DISABLE CONSTRAINT #{constraint["constraint_name"]}"
476
+ end
477
+ yield
478
+ ensure
479
+ old_constraints.each do |constraint|
480
+ execute "ALTER TABLE #{constraint["table_name"]} ENABLE CONSTRAINT #{constraint["constraint_name"]}"
481
+ end
482
+ end
483
+ end
484
+
485
+ private
486
+
487
+ def create_alter_table(name)
488
+ OracleEnhanced::AlterTable.new create_table_definition(name, false, {})
489
+ end
490
+
491
+ def tablespace_for(obj_type, tablespace_option, table_name=nil, column_name=nil)
492
+ tablespace_sql = ''
493
+ if tablespace = (tablespace_option || default_tablespace_for(obj_type))
494
+ tablespace_sql << if [:blob, :clob].include?(obj_type.to_sym)
495
+ " LOB (#{quote_column_name(column_name)}) STORE AS #{column_name.to_s[0..10]}_#{table_name.to_s[0..14]}_ls (TABLESPACE #{tablespace})"
496
+ else
497
+ " TABLESPACE #{tablespace}"
498
+ end
499
+ end
500
+ tablespace_sql
501
+ end
502
+
503
+ def default_tablespace_for(type)
504
+ (default_tablespaces[type] || default_tablespaces[native_database_types[type][:name]]) rescue nil
505
+ end
506
+
507
+
508
+ def column_for(table_name, column_name)
509
+ unless column = columns(table_name).find { |c| c.name == column_name.to_s }
510
+ raise "No such column: #{table_name}.#{column_name}"
511
+ end
512
+ column
513
+ end
514
+
515
+ def create_sequence_and_trigger(table_name, options)
516
+ seq_name = options[:sequence_name] || default_sequence_name(table_name)
517
+ seq_start_value = options[:sequence_start_value] || default_sequence_start_value
518
+ execute "CREATE SEQUENCE #{quote_table_name(seq_name)} START WITH #{seq_start_value}"
519
+
520
+ create_primary_key_trigger(table_name, options) if options[:primary_key_trigger]
521
+ end
522
+
523
+ def create_primary_key_trigger(table_name, options)
524
+ seq_name = options[:sequence_name] || default_sequence_name(table_name)
525
+ trigger_name = options[:trigger_name] || default_trigger_name(table_name)
526
+ primary_key = options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)
527
+ execute compress_lines(<<-SQL)
528
+ CREATE OR REPLACE TRIGGER #{quote_table_name(trigger_name)}
529
+ BEFORE INSERT ON #{quote_table_name(table_name)} FOR EACH ROW
530
+ BEGIN
531
+ IF inserting THEN
532
+ IF :new.#{quote_column_name(primary_key)} IS NULL THEN
533
+ SELECT #{quote_table_name(seq_name)}.NEXTVAL INTO :new.#{quote_column_name(primary_key)} FROM dual;
534
+ END IF;
535
+ END IF;
536
+ END;
537
+ SQL
538
+ end
539
+
540
+ def default_trigger_name(table_name)
541
+ # truncate table name if necessary to fit in max length of identifier
542
+ "#{table_name.to_s[0,table_name_length-4]}_pkt"
543
+ end
544
+
545
+ end
546
+ end
547
+ end
548
+ end