activerecord-oracle_enhanced-adapter 1.2.1 → 1.2.2

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 (30) hide show
  1. data/History.txt +34 -0
  2. data/README.rdoc +10 -5
  3. data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +1 -1
  4. data/lib/active_record/connection_adapters/oracle_enhanced.rake +4 -0
  5. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +534 -170
  6. data/lib/active_record/connection_adapters/oracle_enhanced_connection.rb +53 -3
  7. data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +10 -10
  8. data/lib/active_record/connection_adapters/oracle_enhanced_cpk.rb +3 -3
  9. data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +3 -3
  10. data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +86 -58
  11. data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +105 -68
  12. data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +27 -1
  13. data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +164 -0
  14. data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +122 -0
  15. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +224 -0
  16. data/lib/active_record/connection_adapters/oracle_enhanced_tasks.rb +2 -2
  17. data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +1 -1
  18. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +230 -455
  19. data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +37 -1
  20. data/spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb +1 -1
  21. data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +6 -2
  22. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +21 -4
  23. data/spec/active_record/connection_adapters/oracle_enhanced_dbms_output_spec.rb +63 -0
  24. data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +1 -1
  25. data/spec/active_record/connection_adapters/oracle_enhanced_emulate_oracle_adapter_spec.rb +1 -3
  26. data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +1 -1
  27. data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +255 -0
  28. data/spec/active_record/connection_adapters/oracle_enhanced_schema_spec.rb +720 -0
  29. data/spec/spec_helper.rb +38 -7
  30. metadata +13 -15
@@ -12,16 +12,42 @@ module ActiveRecord #:nodoc:
12
12
  module OracleEnhancedProcedures #:nodoc:
13
13
 
14
14
  module ClassMethods
15
+ # Specify custom create method which should be used instead of Rails generated INSERT statement.
16
+ # Provided block should return ID of new record.
17
+ # Example:
18
+ # set_create_method do
19
+ # plsql.employees_pkg.create_employee(
20
+ # :p_first_name => first_name,
21
+ # :p_last_name => last_name,
22
+ # :p_employee_id => nil
23
+ # )[:p_employee_id]
24
+ # end
15
25
  def set_create_method(&block)
16
26
  include_with_custom_methods
17
27
  self.custom_create_method = block
18
28
  end
19
29
 
30
+ # Specify custom update method which should be used instead of Rails generated UPDATE statement.
31
+ # Example:
32
+ # set_update_method do
33
+ # plsql.employees_pkg.update_employee(
34
+ # :p_employee_id => id,
35
+ # :p_first_name => first_name,
36
+ # :p_last_name => last_name
37
+ # )
38
+ # end
20
39
  def set_update_method(&block)
21
40
  include_with_custom_methods
22
41
  self.custom_update_method = block
23
42
  end
24
43
 
44
+ # Specify custom delete method which should be used instead of Rails generated DELETE statement.
45
+ # Example:
46
+ # set_delete_method do
47
+ # plsql.employees_pkg.delete_employee(
48
+ # :p_employee_id => id
49
+ # )
50
+ # end
25
51
  def set_delete_method(&block)
26
52
  include_with_custom_methods
27
53
  self.custom_delete_method = block
@@ -35,7 +61,7 @@ module ActiveRecord #:nodoc:
35
61
  end
36
62
  end
37
63
 
38
- module InstanceMethods
64
+ module InstanceMethods #:nodoc:
39
65
  def self.included(base)
40
66
  base.instance_eval do
41
67
  if private_instance_methods.include?('create_without_callbacks') || private_instance_methods.include?(:create_without_callbacks)
@@ -0,0 +1,164 @@
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
+ module OracleEnhancedSchemaDefinitions #:nodoc:
10
+ def self.included(base)
11
+ base::TableDefinition.class_eval do
12
+ include OracleEnhancedTableDefinition
13
+ end
14
+
15
+ base::Table.class_eval do
16
+ include OracleEnhancedTable
17
+ end
18
+ end
19
+ end
20
+
21
+ module OracleEnhancedTableDefinition
22
+ class ForeignKey < Struct.new(:base, :to_table, :options) #:nodoc:
23
+ def to_sql
24
+ base.foreign_key_definition(to_table, options)
25
+ end
26
+ alias to_s :to_sql
27
+ end
28
+
29
+ def self.included(base) #:nodoc:
30
+ base.class_eval do
31
+ alias_method_chain :references, :foreign_keys
32
+ alias_method_chain :to_sql, :foreign_keys
33
+ end
34
+ end
35
+
36
+ # Adds a :foreign_key option to TableDefinition.references.
37
+ # If :foreign_key is true, a foreign key constraint is added to the table.
38
+ # You can also specify a hash, which is passed as foreign key options.
39
+ #
40
+ # ===== Examples
41
+ # ====== Add goat_id column and a foreign key to the goats table.
42
+ # t.references(:goat, :foreign_key => true)
43
+ # ====== Add goat_id column and a cascading foreign key to the goats table.
44
+ # t.references(:goat, :foreign_key => {:dependent => :delete})
45
+ #
46
+ # Note: No foreign key is created if :polymorphic => true is used.
47
+ # Note: If no name is specified, the database driver creates one for you!
48
+ def references_with_foreign_keys(*args)
49
+ options = args.extract_options!
50
+ fk_options = options.delete(:foreign_key)
51
+
52
+ if fk_options && !options[:polymorphic]
53
+ fk_options = {} if fk_options == true
54
+ args.each { |to_table| foreign_key(to_table, fk_options) }
55
+ end
56
+
57
+ references_without_foreign_keys(*(args << options))
58
+ end
59
+
60
+ # Defines a foreign key for the table. +to_table+ can be a single Symbol, or
61
+ # an Array of Symbols. See SchemaStatements#add_foreign_key
62
+ #
63
+ # ===== Examples
64
+ # ====== Creating a simple foreign key
65
+ # t.foreign_key(:people)
66
+ # ====== Defining the column
67
+ # t.foreign_key(:people, :column => :sender_id)
68
+ # ====== Creating a named foreign key
69
+ # t.foreign_key(:people, :column => :sender_id, :name => 'sender_foreign_key')
70
+ # ====== Defining the column of the +to_table+.
71
+ # t.foreign_key(:people, :column => :sender_id, :primary_key => :person_id)
72
+ def foreign_key(to_table, options = {})
73
+ if @base.respond_to?(:supports_foreign_keys?) && @base.supports_foreign_keys?
74
+ to_table = to_table.to_s.pluralize if ActiveRecord::Base.pluralize_table_names
75
+ foreign_keys << ForeignKey.new(@base, to_table, options)
76
+ else
77
+ raise ArgumentError, "this ActiveRecord adapter is not supporting foreign_key definition"
78
+ end
79
+ end
80
+
81
+ def to_sql_with_foreign_keys #:nodoc:
82
+ sql = to_sql_without_foreign_keys
83
+ sql << ', ' << (foreign_keys * ', ') if foreign_keys.present?
84
+ sql
85
+ end
86
+
87
+ private
88
+ def foreign_keys
89
+ @foreign_keys ||= []
90
+ end
91
+ end
92
+
93
+ module OracleEnhancedTable
94
+ def self.included(base) #:nodoc:
95
+ base.class_eval do
96
+ alias_method_chain :references, :foreign_keys
97
+ end
98
+ end
99
+
100
+ # Adds a new foreign key to the table. +to_table+ can be a single Symbol, or
101
+ # an Array of Symbols. See SchemaStatements#add_foreign_key
102
+ #
103
+ # ===== Examples
104
+ # ====== Creating a simple foreign key
105
+ # t.foreign_key(:people)
106
+ # ====== Defining the column
107
+ # t.foreign_key(:people, :column => :sender_id)
108
+ # ====== Creating a named foreign key
109
+ # t.foreign_key(:people, :column => :sender_id, :name => 'sender_foreign_key')
110
+ # ====== Defining the column of the +to_table+.
111
+ # t.foreign_key(:people, :column => :sender_id, :primary_key => :person_id)
112
+ def foreign_key(to_table, options = {})
113
+ if @base.respond_to?(:supports_foreign_keys?) && @base.supports_foreign_keys?
114
+ to_table = to_table.to_s.pluralize if ActiveRecord::Base.pluralize_table_names
115
+ @base.add_foreign_key(@table_name, to_table, options)
116
+ else
117
+ raise ArgumentError, "this ActiveRecord adapter is not supporting foreign_key definition"
118
+ end
119
+ end
120
+
121
+ # Remove the given foreign key from the table.
122
+ #
123
+ # ===== Examples
124
+ # ====== Remove the suppliers_company_id_fk in the suppliers table.
125
+ # t.remove_foreign_key :companies
126
+ # ====== Remove the foreign key named accounts_branch_id_fk in the accounts table.
127
+ # remove_foreign_key :column => :branch_id
128
+ # ====== Remove the foreign key named party_foreign_key in the accounts table.
129
+ # remove_index :name => :party_foreign_key
130
+ def remove_foreign_key(options = {})
131
+ @base.remove_foreign_key(@table_name, options)
132
+ end
133
+
134
+ # Adds a :foreign_key option to TableDefinition.references.
135
+ # If :foreign_key is true, a foreign key constraint is added to the table.
136
+ # You can also specify a hash, which is passed as foreign key options.
137
+ #
138
+ # ===== Examples
139
+ # ====== Add goat_id column and a foreign key to the goats table.
140
+ # t.references(:goat, :foreign_key => true)
141
+ # ====== Add goat_id column and a cascading foreign key to the goats table.
142
+ # t.references(:goat, :foreign_key => {:dependent => :delete})
143
+ #
144
+ # Note: No foreign key is created if :polymorphic => true is used.
145
+ def references_with_foreign_keys(*args)
146
+ options = args.extract_options!
147
+ polymorphic = options[:polymorphic]
148
+ fk_options = options.delete(:foreign_key)
149
+
150
+ references_without_foreign_keys(*(args << options))
151
+ # references_without_foreign_keys adds {:type => :integer}
152
+ args.extract_options!
153
+ if fk_options && !polymorphic
154
+ fk_options = {} if fk_options == true
155
+ args.each { |to_table| foreign_key(to_table, fk_options) }
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ ActiveRecord::ConnectionAdapters.class_eval do
163
+ include ActiveRecord::ConnectionAdapters::OracleEnhancedSchemaDefinitions
164
+ end
@@ -0,0 +1,122 @@
1
+ module ActiveRecord #:nodoc:
2
+ module ConnectionAdapters #:nodoc:
3
+ module OracleEnhancedSchemaDumper #:nodoc:
4
+
5
+ def self.included(base) #:nodoc:
6
+ base.class_eval do
7
+ private
8
+ alias_method_chain :tables, :oracle_enhanced
9
+ alias_method_chain :indexes, :oracle_enhanced
10
+ end
11
+ end
12
+
13
+ private
14
+
15
+ def tables_with_oracle_enhanced(stream)
16
+ @connection.tables.sort.each do |tbl|
17
+ # add table prefix or suffix for schema_migrations
18
+ next if [ActiveRecord::Migrator.proper_table_name('schema_migrations'), ignore_tables].flatten.any? do |ignored|
19
+ case ignored
20
+ when String; tbl == ignored
21
+ when Regexp; tbl =~ ignored
22
+ else
23
+ raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.'
24
+ end
25
+ end
26
+ # change table name inspect method
27
+ tbl.extend TableInspect
28
+ table(tbl, stream)
29
+ # add primary key trigger if table has it
30
+ primary_key_trigger(tbl, stream)
31
+ # add foreign keys if table has them
32
+ foreign_keys(tbl, stream)
33
+ end
34
+ synonyms(stream)
35
+ end
36
+
37
+ def primary_key_trigger(table_name, stream)
38
+ if @connection.respond_to?(:has_primary_key_trigger?) && @connection.has_primary_key_trigger?(table_name)
39
+ pk, pk_seq = @connection.pk_and_sequence_for(table_name)
40
+ stream.print " add_primary_key_trigger #{table_name.inspect}"
41
+ stream.print ", :primary_key => \"#{pk}\"" if pk != 'id'
42
+ stream.print "\n\n"
43
+ end
44
+ end
45
+
46
+ def foreign_keys(table_name, stream)
47
+ if (foreign_keys = @connection.foreign_keys(table_name)).any?
48
+ add_foreign_key_statements = foreign_keys.map do |foreign_key|
49
+ statement_parts = [ ('add_foreign_key ' + foreign_key.from_table.inspect) ]
50
+ statement_parts << foreign_key.to_table.inspect
51
+ statement_parts << (':name => ' + foreign_key.options[:name].inspect)
52
+
53
+ if foreign_key.options[:column] != "#{foreign_key.to_table.singularize}_id"
54
+ statement_parts << (':column => ' + foreign_key.options[:column].inspect)
55
+ end
56
+ if foreign_key.options[:primary_key] != 'id'
57
+ statement_parts << (':primary_key => ' + foreign_key.options[:primary_key].inspect)
58
+ end
59
+ if foreign_key.options[:dependent].present?
60
+ statement_parts << (':dependent => ' + foreign_key.options[:dependent].inspect)
61
+ end
62
+
63
+ ' ' + statement_parts.join(', ')
64
+ end
65
+
66
+ stream.puts add_foreign_key_statements.sort.join("\n")
67
+ stream.puts
68
+ end
69
+ end
70
+
71
+ def synonyms(stream)
72
+ syns = @connection.synonyms
73
+ syns.each do |syn|
74
+ table_name = syn.table_name
75
+ table_name = "#{syn.table_owner}.#{table_name}" if syn.table_owner
76
+ table_name = "#{table_name}@#{syn.db_link}" if syn.db_link
77
+ stream.print " add_synonym #{syn.name.inspect}, #{table_name.inspect}, :force => true"
78
+ stream.puts
79
+ end
80
+ stream.puts unless syns.empty?
81
+ end
82
+
83
+ def indexes_with_oracle_enhanced(table, stream)
84
+ if (indexes = @connection.indexes(table)).any?
85
+ add_index_statements = indexes.map do |index|
86
+ # use table.inspect as it will remove prefix and suffix
87
+ statment_parts = [ ('add_index ' + table.inspect) ]
88
+ statment_parts << index.columns.inspect
89
+ statment_parts << (':name => ' + index.name.inspect)
90
+ statment_parts << ':unique => true' if index.unique
91
+
92
+ ' ' + statment_parts.join(', ')
93
+ end
94
+
95
+ stream.puts add_index_statements.sort.join("\n")
96
+ stream.puts
97
+ end
98
+ end
99
+
100
+ # remove table name prefix and suffix when doing #inspect (which is used in tables method)
101
+ module TableInspect #:nodoc:
102
+ def inspect
103
+ remove_prefix_and_suffix(self)
104
+ end
105
+
106
+ private
107
+ def remove_prefix_and_suffix(table_name)
108
+ if table_name =~ /\A#{ActiveRecord::Base.table_name_prefix}(.*)#{ActiveRecord::Base.table_name_suffix}\Z/
109
+ "\"#{$1}\""
110
+ else
111
+ "\"#{table_name}\""
112
+ end
113
+ end
114
+ end
115
+
116
+ end
117
+ end
118
+ end
119
+
120
+ ActiveRecord::SchemaDumper.class_eval do
121
+ include ActiveRecord::ConnectionAdapters::OracleEnhancedSchemaDumper
122
+ end
@@ -0,0 +1,224 @@
1
+ require 'digest/sha1'
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ module OracleEnhancedSchemaStatementsExt
6
+ def supports_foreign_keys? #:nodoc:
7
+ true
8
+ end
9
+
10
+ # Create primary key trigger (so that you can skip primary key value in INSERT statement).
11
+ # By default trigger name will be "table_name_pkt", you can override the name with
12
+ # :trigger_name option (but it is not recommended to override it as then this trigger will
13
+ # not be detected by ActiveRecord model and it will still do prefetching of sequence value).
14
+ #
15
+ # add_primary_key_trigger :users
16
+ #
17
+ # You can also create primary key trigger using +create_table+ with :primary_key_trigger
18
+ # option:
19
+ #
20
+ # create_table :users, :primary_key_trigger => true do |t|
21
+ # # ...
22
+ # end
23
+ #
24
+ def add_primary_key_trigger(table_name, options)
25
+ # call the same private method that is used for create_table :primary_key_trigger => true
26
+ create_primary_key_trigger(table_name, options)
27
+ end
28
+
29
+ # Adds a new foreign key to the +from_table+, referencing the primary key of +to_table+
30
+ # (syntax and partial implementation taken from http://github.com/matthuhiggins/foreigner)
31
+ #
32
+ # The foreign key will be named after the from and to tables unless you pass
33
+ # <tt>:name</tt> as an option.
34
+ #
35
+ # === Examples
36
+ # ==== Creating a foreign key
37
+ # add_foreign_key(:comments, :posts)
38
+ # generates
39
+ # ALTER TABLE comments ADD CONSTRAINT
40
+ # comments_post_id_fk FOREIGN KEY (post_id) REFERENCES posts (id)
41
+ #
42
+ # ==== Creating a named foreign key
43
+ # add_foreign_key(:comments, :posts, :name => 'comments_belongs_to_posts')
44
+ # generates
45
+ # ALTER TABLE comments ADD CONSTRAINT
46
+ # comments_belongs_to_posts FOREIGN KEY (post_id) REFERENCES posts (id)
47
+ #
48
+ # ==== Creating a cascading foreign_key on a custom column
49
+ # add_foreign_key(:people, :people, :column => 'best_friend_id', :dependent => :nullify)
50
+ # generates
51
+ # ALTER TABLE people ADD CONSTRAINT
52
+ # people_best_friend_id_fk FOREIGN KEY (best_friend_id) REFERENCES people (id)
53
+ # ON DELETE SET NULL
54
+ #
55
+ # === Supported options
56
+ # [:column]
57
+ # Specify the column name on the from_table that references the to_table. By default this is guessed
58
+ # to be the singular name of the to_table with "_id" suffixed. So a to_table of :posts will use "post_id"
59
+ # as the default <tt>:column</tt>.
60
+ # [:primary_key]
61
+ # Specify the column name on the to_table that is referenced by this foreign key. By default this is
62
+ # assumed to be "id".
63
+ # [:name]
64
+ # Specify the name of the foreign key constraint. This defaults to use from_table and foreign key column.
65
+ # [:dependent]
66
+ # If set to <tt>:delete</tt>, the associated records in from_table are deleted when records in to_table table are deleted.
67
+ # If set to <tt>:nullify</tt>, the foreign key column is set to +NULL+.
68
+ def add_foreign_key(from_table, to_table, options = {})
69
+ column = options[:column] || "#{to_table.to_s.singularize}_id"
70
+ constraint_name = foreign_key_constraint_name(from_table, column, options)
71
+ sql = "ALTER TABLE #{quote_table_name(from_table)} ADD CONSTRAINT #{quote_column_name(constraint_name)} "
72
+ sql << foreign_key_definition(to_table, options)
73
+ execute sql
74
+ end
75
+
76
+ def foreign_key_definition(to_table, options = {}) #:nodoc:
77
+ column = options[:column] || "#{to_table.to_s.singularize}_id"
78
+ primary_key = options[:primary_key] || "id"
79
+ sql = "FOREIGN KEY (#{quote_column_name(column)}) REFERENCES #{quote_table_name(to_table)}(#{primary_key})"
80
+ case options[:dependent]
81
+ when :nullify
82
+ sql << " ON DELETE SET NULL"
83
+ when :delete
84
+ sql << " ON DELETE CASCADE"
85
+ end
86
+ sql
87
+ end
88
+
89
+ # Remove the given foreign key from the table.
90
+ #
91
+ # ===== Examples
92
+ # ====== Remove the suppliers_company_id_fk in the suppliers table.
93
+ # remove_foreign_key :suppliers, :companies
94
+ # ====== Remove the foreign key named accounts_branch_id_fk in the accounts table.
95
+ # remove_foreign_key :accounts, :column => :branch_id
96
+ # ====== Remove the foreign key named party_foreign_key in the accounts table.
97
+ # remove_foreign_key :accounts, :name => :party_foreign_key
98
+ def remove_foreign_key(from_table, options)
99
+ if Hash === options
100
+ constraint_name = foreign_key_constraint_name(from_table, options[:column], options)
101
+ else
102
+ constraint_name = foreign_key_constraint_name(from_table, "#{options.to_s.singularize}_id")
103
+ end
104
+ execute "ALTER TABLE #{quote_table_name(from_table)} DROP CONSTRAINT #{quote_column_name(constraint_name)}"
105
+ end
106
+
107
+ private
108
+
109
+ def foreign_key_constraint_name(table_name, column, options = {})
110
+ constraint_name = original_name = options[:name] || "#{table_name}_#{column}_fk"
111
+ return constraint_name if constraint_name.length <= OracleEnhancedAdapter::IDENTIFIER_MAX_LENGTH
112
+ # leave just first three letters from each word
113
+ constraint_name = constraint_name.split('_').map{|w| w[0,3]}.join('_')
114
+ # generate unique name using hash function
115
+ if constraint_name.length > OracleEnhancedAdapter::IDENTIFIER_MAX_LENGTH
116
+ constraint_name = 'c'+Digest::SHA1.hexdigest(original_name)[0,OracleEnhancedAdapter::IDENTIFIER_MAX_LENGTH-1]
117
+ end
118
+ @logger.warn "#{adapter_name} shortened foreign key constraint name #{original_name} to #{constraint_name}" if @logger
119
+ constraint_name
120
+ end
121
+
122
+
123
+ public
124
+
125
+ # get table foreign keys for schema dump
126
+ def foreign_keys(table_name) #:nodoc:
127
+ (owner, desc_table_name, db_link) = @connection.describe(table_name)
128
+
129
+ fk_info = select_all(<<-SQL, 'Foreign Keys')
130
+ SELECT r.table_name to_table
131
+ ,rc.column_name primary_key
132
+ ,cc.column_name
133
+ ,c.constraint_name name
134
+ ,c.delete_rule
135
+ FROM user_constraints#{db_link} c, user_cons_columns#{db_link} cc,
136
+ user_constraints#{db_link} r, user_cons_columns#{db_link} rc
137
+ WHERE c.owner = '#{owner}'
138
+ AND c.table_name = '#{desc_table_name}'
139
+ AND c.constraint_type = 'R'
140
+ AND cc.owner = c.owner
141
+ AND cc.constraint_name = c.constraint_name
142
+ AND r.constraint_name = c.r_constraint_name
143
+ AND r.owner = c.owner
144
+ AND rc.owner = r.owner
145
+ AND rc.constraint_name = r.constraint_name
146
+ AND rc.position = cc.position
147
+ SQL
148
+
149
+ fk_info.map do |row|
150
+ options = {:column => oracle_downcase(row['column_name']), :name => oracle_downcase(row['name']),
151
+ :primary_key => oracle_downcase(row['primary_key'])}
152
+ case row['delete_rule']
153
+ when 'CASCADE'
154
+ options[:dependent] = :delete
155
+ when 'SET NULL'
156
+ options[:dependent] = :nullify
157
+ end
158
+ OracleEnhancedForeignKeyDefinition.new(table_name, oracle_downcase(row['to_table']), options)
159
+ end
160
+ end
161
+
162
+ # REFERENTIAL INTEGRITY ====================================
163
+
164
+ def disable_referential_integrity(&block) #:nodoc:
165
+ sql_constraints = <<-SQL
166
+ SELECT constraint_name, owner, table_name
167
+ FROM user_constraints
168
+ WHERE constraint_type = 'R'
169
+ AND status = 'ENABLED'
170
+ SQL
171
+ old_constraints = select_all(sql_constraints)
172
+ begin
173
+ old_constraints.each do |constraint|
174
+ execute "ALTER TABLE #{constraint["table_name"]} DISABLE CONSTRAINT #{constraint["constraint_name"]}"
175
+ end
176
+ yield
177
+ ensure
178
+ old_constraints.each do |constraint|
179
+ execute "ALTER TABLE #{constraint["table_name"]} ENABLE CONSTRAINT #{constraint["constraint_name"]}"
180
+ end
181
+ end
182
+ end
183
+
184
+ # Add synonym to existing table or view or sequence. Can be used to create local synonym to
185
+ # remote table in other schema or in other database
186
+ # Examples:
187
+ #
188
+ # add_synonym :posts, "blog.posts"
189
+ # add_synonym :posts_seq, "blog.posts_seq"
190
+ # add_synonym :employees, "hr.employees@dblink", :force => true
191
+ #
192
+ def add_synonym(name, table_name, options = {})
193
+ sql = "CREATE"
194
+ if options[:force] == true
195
+ sql << " OR REPLACE"
196
+ end
197
+ sql << " SYNONYM #{quote_table_name(name)} FOR #{quote_table_name(table_name)}"
198
+ execute sql
199
+ end
200
+
201
+ # Remove existing synonym to table or view or sequence
202
+ # Example:
203
+ #
204
+ # remove_synonym :posts, "blog.posts"
205
+ #
206
+ def remove_synonym(name)
207
+ execute "DROP SYNONYM #{quote_table_name(name)}"
208
+ end
209
+
210
+ # get synonyms for schema dump
211
+ def synonyms #:nodoc:
212
+ select_all("SELECT synonym_name, table_owner, table_name, db_link FROM user_synonyms").collect do |row|
213
+ OracleEnhancedSynonymDefinition.new(oracle_downcase(row['synonym_name']),
214
+ oracle_downcase(row['table_owner']), oracle_downcase(row['table_name']), oracle_downcase(row['db_link']))
215
+ end
216
+ end
217
+
218
+ end
219
+ end
220
+ end
221
+
222
+ ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do
223
+ include ActiveRecord::ConnectionAdapters::OracleEnhancedSchemaStatementsExt
224
+ end