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.
- data/History.txt +34 -0
- data/README.rdoc +10 -5
- data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +1 -1
- data/lib/active_record/connection_adapters/oracle_enhanced.rake +4 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +534 -170
- data/lib/active_record/connection_adapters/oracle_enhanced_connection.rb +53 -3
- data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +10 -10
- data/lib/active_record/connection_adapters/oracle_enhanced_cpk.rb +3 -3
- data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +3 -3
- data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +86 -58
- data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +105 -68
- data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +27 -1
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +164 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +122 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +224 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_tasks.rb +2 -2
- data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +1 -1
- data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +230 -455
- data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +37 -1
- data/spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb +1 -1
- data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +6 -2
- data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +21 -4
- data/spec/active_record/connection_adapters/oracle_enhanced_dbms_output_spec.rb +63 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +1 -1
- data/spec/active_record/connection_adapters/oracle_enhanced_emulate_oracle_adapter_spec.rb +1 -3
- data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +1 -1
- data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +255 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_schema_spec.rb +720 -0
- data/spec/spec_helper.rb +38 -7
- 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
|