activerecord-oracle_enhanced-adapter 1.5.6 → 1.6.0.beta1

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -2
  3. data/History.md +87 -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_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} +0 -0
  18. data/lib/active_record/connection_adapters/{oracle_enhanced_schema_creation.rb → oracle_enhanced/schema_creation.rb} +17 -16
  19. data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +92 -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 +543 -0
  22. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements_ext.rb +65 -0
  23. data/lib/active_record/connection_adapters/{oracle_enhanced_structure_dump.rb → oracle_enhanced/structure_dump.rb} +26 -4
  24. data/lib/active_record/connection_adapters/oracle_enhanced/version.rb +1 -0
  25. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +159 -66
  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/activerecord-oracle_enhanced-adapter.rb +1 -1
  30. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +6 -31
  31. data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +1 -1
  32. data/spec/active_record/connection_adapters/oracle_enhanced_context_index_spec.rb +2 -2
  33. data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +2 -2
  34. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +63 -63
  35. data/spec/active_record/connection_adapters/oracle_enhanced_database_tasks_spec.rb +1 -1
  36. data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +7 -13
  37. data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +21 -175
  38. data/spec/spec_config.yaml.template +10 -0
  39. data/spec/spec_helper.rb +21 -10
  40. metadata +29 -25
  41. data/lib/active_record/connection_adapters/oracle_enhanced_column_dumper.rb +0 -77
  42. data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +0 -350
  43. data/lib/active_record/connection_adapters/oracle_enhanced_database_statements.rb +0 -262
  44. data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +0 -45
  45. data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +0 -197
  46. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb +0 -450
  47. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +0 -258
  48. data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +0 -1
@@ -1,45 +0,0 @@
1
- module ActiveRecord #:nodoc:
2
- module ConnectionAdapters #:nodoc:
3
- module OracleEnhancedDirty #:nodoc:
4
-
5
- module InstanceMethods #:nodoc:
6
- private
7
-
8
- def _field_changed?(attr, old, value)
9
- if column = column_for_attribute(attr)
10
- # Added also :decimal type
11
- if ([:integer, :decimal, :float].include? column.type) && column.null && (old.nil? || old == 0) && value.blank?
12
- # For nullable integer/decimal/float columns, NULL gets stored in database for blank (i.e. '') values.
13
- # Hence we don't record it as a change if the value changes from nil to ''.
14
- # If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
15
- # be typecast back to 0 (''.to_i => 0)
16
- value = nil
17
- elsif column.type == :string && column.null && old.nil?
18
- # Oracle stores empty string '' as NULL
19
- # therefore need to convert empty string value to nil if old value is nil
20
- value = nil if value == ''
21
- elsif old == 0 && value.is_a?(String) && value.present? && non_zero?(value)
22
- value = nil
23
- else
24
- value = column.type_cast(value)
25
- end
26
- end
27
-
28
- old != value
29
- end
30
-
31
- def non_zero?(value)
32
- value !~ /\A0+(\.0+)?\z/
33
- end
34
-
35
- end
36
-
37
- end
38
- end
39
- end
40
-
41
- if ActiveRecord::Base.method_defined?(:changed?)
42
- ActiveRecord::Base.class_eval do
43
- include ActiveRecord::ConnectionAdapters::OracleEnhancedDirty::InstanceMethods
44
- end
45
- end
@@ -1,197 +0,0 @@
1
- module ActiveRecord
2
- module ConnectionAdapters
3
- class OracleEnhancedForeignKeyDefinition < Struct.new(:from_table, :to_table, :options) #:nodoc:
4
- end
5
-
6
- class OracleEnhancedSynonymDefinition < Struct.new(:name, :table_owner, :table_name, :db_link) #:nodoc:
7
- end
8
-
9
- class OracleEnhancedIndexDefinition < Struct.new(:table, :name, :unique, :type, :parameters, :statement_parameters,
10
- :tablespace, :columns) #:nodoc:
11
- end
12
-
13
- module OracleEnhancedSchemaDefinitions #:nodoc:
14
- def self.included(base)
15
- base::TableDefinition.class_eval do
16
- include OracleEnhancedTableDefinition
17
- end
18
-
19
- # Available starting from ActiveRecord 2.1
20
- base::Table.class_eval do
21
- include OracleEnhancedTable
22
- end if defined?(base::Table)
23
- end
24
- end
25
-
26
- module OracleEnhancedTableDefinition
27
- class ForeignKey < Struct.new(:base, :to_table, :options) #:nodoc:
28
- def to_sql
29
- base.foreign_key_definition(to_table, options)
30
- end
31
- alias to_s :to_sql
32
- end
33
-
34
- def self.included(base) #:nodoc:
35
- base.class_eval do
36
- alias_method_chain :references, :foreign_keys
37
- alias_method_chain :column, :virtual_columns
38
- end
39
- end
40
-
41
- def raw(name, options={})
42
- column(name, :raw, options)
43
- end
44
-
45
- def virtual(* args)
46
- options = args.extract_options!
47
- column_names = args
48
- column_names.each { |name| column(name, :virtual, options) }
49
- end
50
-
51
- def column_with_virtual_columns(name, type, options = {})
52
- if type == :virtual
53
- default = {:type => options[:type]}
54
- if options[:as]
55
- default[:as] = options[:as]
56
- elsif options[:default]
57
- warn "[DEPRECATION] virtual column `:default` option is deprecated. Please use `:as` instead."
58
- default[:as] = options[:default]
59
- else
60
- raise "No virtual column definition found."
61
- end
62
- options[:default] = default
63
- end
64
- column_without_virtual_columns(name, type, options)
65
- end
66
-
67
- # Adds a :foreign_key option to TableDefinition.references.
68
- # If :foreign_key is true, a foreign key constraint is added to the table.
69
- # You can also specify a hash, which is passed as foreign key options.
70
- #
71
- # ===== Examples
72
- # ====== Add goat_id column and a foreign key to the goats table.
73
- # t.references(:goat, :foreign_key => true)
74
- # ====== Add goat_id column and a cascading foreign key to the goats table.
75
- # t.references(:goat, :foreign_key => {:dependent => :delete})
76
- #
77
- # Note: No foreign key is created if :polymorphic => true is used.
78
- # Note: If no name is specified, the database driver creates one for you!
79
- def references_with_foreign_keys(*args)
80
- options = args.extract_options!
81
- index_options = options[:index]
82
- fk_options = options.delete(:foreign_key)
83
-
84
- if fk_options && !options[:polymorphic]
85
- fk_options = {} if fk_options == true
86
- args.each do |to_table|
87
- foreign_key(to_table, fk_options)
88
- add_index(to_table, "#{to_table}_id", index_options.is_a?(Hash) ? index_options : nil) if index_options
89
- end
90
- end
91
-
92
- references_without_foreign_keys(*(args << options))
93
- end
94
-
95
- # Defines a foreign key for the table. +to_table+ can be a single Symbol, or
96
- # an Array of Symbols. See SchemaStatements#add_foreign_key
97
- #
98
- # ===== Examples
99
- # ====== Creating a simple foreign key
100
- # t.foreign_key(:people)
101
- # ====== Defining the column
102
- # t.foreign_key(:people, :column => :sender_id)
103
- # ====== Creating a named foreign key
104
- # t.foreign_key(:people, :column => :sender_id, :name => 'sender_foreign_key')
105
- # ====== Defining the column of the +to_table+.
106
- # t.foreign_key(:people, :column => :sender_id, :primary_key => :person_id)
107
- def foreign_key(to_table, options = {})
108
- #TODO
109
- if ActiveRecord::Base.connection.supports_foreign_keys?
110
- to_table = to_table.to_s.pluralize if ActiveRecord::Base.pluralize_table_names
111
- foreign_keys << ForeignKey.new(@base, to_table, options)
112
- else
113
- raise ArgumentError, "this ActiveRecord adapter is not supporting foreign_key definition"
114
- end
115
- end
116
-
117
- def foreign_keys
118
- @foreign_keys ||= []
119
- end
120
- end
121
-
122
- module OracleEnhancedTable
123
- def self.included(base) #:nodoc:
124
- base.class_eval do
125
- alias_method_chain :references, :foreign_keys
126
- end
127
- end
128
-
129
- # Adds a new foreign key to the table. +to_table+ can be a single Symbol, or
130
- # an Array of Symbols. See SchemaStatements#add_foreign_key
131
- #
132
- # ===== Examples
133
- # ====== Creating a simple foreign key
134
- # t.foreign_key(:people)
135
- # ====== Defining the column
136
- # t.foreign_key(:people, :column => :sender_id)
137
- # ====== Creating a named foreign key
138
- # t.foreign_key(:people, :column => :sender_id, :name => 'sender_foreign_key')
139
- # ====== Defining the column of the +to_table+.
140
- # t.foreign_key(:people, :column => :sender_id, :primary_key => :person_id)
141
- def foreign_key(to_table, options = {})
142
- if @base.respond_to?(:supports_foreign_keys?) && @base.supports_foreign_keys?
143
- to_table = to_table.to_s.pluralize if ActiveRecord::Base.pluralize_table_names
144
- @base.add_foreign_key(@table_name, to_table, options)
145
- else
146
- raise ArgumentError, "this ActiveRecord adapter is not supporting foreign_key definition"
147
- end
148
- end
149
-
150
- # Remove the given foreign key from the table.
151
- #
152
- # ===== Examples
153
- # ====== Remove the suppliers_company_id_fk in the suppliers table.
154
- # t.remove_foreign_key :companies
155
- # ====== Remove the foreign key named accounts_branch_id_fk in the accounts table.
156
- # remove_foreign_key :column => :branch_id
157
- # ====== Remove the foreign key named party_foreign_key in the accounts table.
158
- # remove_index :name => :party_foreign_key
159
- def remove_foreign_key(options = {})
160
- @base.remove_foreign_key(@table_name, options)
161
- end
162
-
163
- # Adds a :foreign_key option to TableDefinition.references.
164
- # If :foreign_key is true, a foreign key constraint is added to the table.
165
- # You can also specify a hash, which is passed as foreign key options.
166
- #
167
- # ===== Examples
168
- # ====== Add goat_id column and a foreign key to the goats table.
169
- # t.references(:goat, :foreign_key => true)
170
- # ====== Add goat_id column and a cascading foreign key to the goats table.
171
- # t.references(:goat, :foreign_key => {:dependent => :delete})
172
- #
173
- # Note: No foreign key is created if :polymorphic => true is used.
174
- def references_with_foreign_keys(*args)
175
- options = args.extract_options!
176
- polymorphic = options[:polymorphic]
177
- index_options = options[:index]
178
- fk_options = options.delete(:foreign_key)
179
-
180
- references_without_foreign_keys(*(args << options))
181
- # references_without_foreign_keys adds {:type => :integer}
182
- args.extract_options!
183
- if fk_options && !polymorphic
184
- fk_options = {} if fk_options == true
185
- args.each do |to_table|
186
- foreign_key(to_table, fk_options)
187
- add_index(to_table, "#{to_table}_id", index_options.is_a?(Hash) ? index_options : nil) if index_options
188
- end
189
- end
190
- end
191
- end
192
- end
193
- end
194
-
195
- ActiveRecord::ConnectionAdapters.class_eval do
196
- include ActiveRecord::ConnectionAdapters::OracleEnhancedSchemaDefinitions
197
- end
@@ -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