activerecord-oracle_enhanced-adapter 1.5.6 → 1.6.0

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