foreign_keys 1.2.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -1,73 +1,64 @@
1
- Foreign Keys Plugin
1
+ Foreign Keys
2
2
  ===================
3
3
 
4
- This plugin adds basic support for foreign keys, by providing some additional
5
- schema statements as well as integration into the standard schema dumper.
6
-
7
- Currently, mysql and postgresql connection adapters are supported.
4
+ Add foreign key functionality for Rails migrations and schema dumps.
5
+ MySQL and PostgreSQL adapters supported.
8
6
 
9
7
  Installation
10
8
  ------------
11
9
 
12
- Via standard git clone:
10
+ As Rails gem (from GemCutter)
13
11
 
14
- git clone git://github.com/dwalters/foreign_keys.git vendor/plugins/foreign_keys
12
+ sudo gem install foreign_keys
15
13
 
16
- Via rails plugin (requires 2.1):
14
+ As Rails plugin
17
15
 
18
- script/plugin install git://github.com/dwalters/foreign_keys.git
16
+ script/plugin install git://github.com/perfectline/foreign_keys.git
19
17
 
20
- Adding Foreign Keys
18
+ Quickstart
21
19
  -------------------
22
20
 
23
- Just place the appropriate statements in your migrations. The simplest example is:
24
-
25
- add_foreign_key :orders, :user_id
26
-
27
- The usual rails conventions are used to deduce that this should result in a
28
- foreign key from the `orders.user_id` column to the `users.id` column
29
-
30
- Of course, you may specify these values explicitly too, and will need to when
31
- the conventions don't match exactly:
21
+ Just place the FK statements in your migrations. The simplest example is:
32
22
 
33
- add_foreign_key :articles, :author_id, :references => :users
23
+ add_foreign_key :albums, :user_id
34
24
 
35
- Some other options of note:
25
+ The referenced table and column are decidec by using rails conventions, i.e this
26
+ example would result in a foreign key from 'albums.user_id' to 'users.id' column.
36
27
 
37
- * `references`: the name of the referenced table
38
- * `keys`: the corresponding column name(s) in the referenced table
39
- * `on_delete` / `on_update`: one of `:restrict`, `:set_null`, `:cascade`
40
- * `name`: the name to assign to the foreign key constraint
28
+ You can additionally provide multiple options for cases where the association information cannot
29
+ be decided by conventions.
41
30
 
42
- Note that recent mysql versions will create an index on the referenced
43
- column(s) if one does not already exist.
31
+ add_foreign_key :albums, :author_id, :references => :users, :keys => :user_id, :name => "my_special_fk"
44
32
 
45
- You may wish to check the documentation for further options.
33
+ Removing foreign keys is just as easy.
34
+ You can remove them either by providing the constraint name or column(s).
46
35
 
47
- Removing Foreign Keys
48
- ---------------------
36
+ remove_foreign_key :albums, :user_id
37
+ remove_foreign_key :albums, :name => "my_special_fk"
49
38
 
50
- You can remove foreign keys by name:
39
+ Note: MySQL creates an index on the FK column automatically. Removing a FK will remove that index also.
51
40
 
52
- remove_foreign_key :orders, :name => "index_orders_on_user_id"
53
-
54
- Or the name can be deduced if you used standard conventions when you created it:
55
-
56
- remove_foreign_key :orders, :user_id
41
+ Options
42
+ -------------------
57
43
 
58
- This will not automatically remove any indexes.
44
+ * **references**: name of the references table
45
+ * **keys**: column name(s) on the references table
46
+ * **on_delete**: on delete hook with a value of `:restrict`, `:set_null`, `:cascade`
47
+ * **on_update**: on update hook with a value of `:restrict`, `:set_null`, `:cascade`
48
+ * **name**: foreign key constraint name
59
49
 
60
50
  Schema Dump
61
- -----------
51
+ ---------------
62
52
 
63
53
  The usual rake targets for `db:schema:dump`, `db:schema:load`, and `db:reset`
64
54
  should all work as desired.
65
55
 
66
- Author
67
- ------
68
-
69
- Dan Walters
56
+ Authors
57
+ ---------------
70
58
 
71
- <http://github.com/dwalters/foreign_keys>
59
+ **Tanel Suurhans** - tanel.suurhans_at_perfectline.ee
60
+ **Tarmo Lehtpuu** - tarmo.lehtpuu_at_perfectline.ee
72
61
 
73
- Max Lapshin -- PostgreSQL support
62
+ License
63
+ --------------
64
+ Copyright 2009 by PerfectLine LLC (<http://www.perfectline.co.uk>) and is released under the MIT license.
@@ -0,0 +1,54 @@
1
+ module Perfectline
2
+ module ConnectionAdapters
3
+ module AbstractAdapter
4
+
5
+ # Adds a foreign key to given table.
6
+ # Columns can be a String or a Symbol, or an Array of either.
7
+ #
8
+ # The references tables and columns are decided using the usual Rails conventions.
9
+ # These can be explicitly overriden via +:references+ and/or +:keys+ options.
10
+ #
11
+ # Name of the FK can be specified via +:name+ option, if this is not provided, foreign_key_name method is used.
12
+ #
13
+ # You can also specify update and delete hooks with +:on_delete+ and +:on_update+ options.
14
+ # Valid values for these are +:restrict+, +:set_null+, +:cascade+.
15
+ #
16
+ # Examples:
17
+ # add_foreign_key :albums, :user_id
18
+ # add_foreign_key :albums, :author_id, :references => :users
19
+ # add_foreign_key :albums, [:first_name, :last_name], :references => :users, :keys => [:first_name, :last_name], :name => "composite_fk"
20
+ def add_foreign_key(table_name, columns, options = {})
21
+ name = options.delete(:name) || foreign_key_name(table_name, Array(columns))
22
+ reference = options.delete(:references) || Array(columns).first.to_s.gsub(/_id$/, '').tableize
23
+ columns = Array(columns).collect{ |column| quote_column_name(column) }.join(", ")
24
+ keys = Array(options.delete(:keys) || :id).collect{ |key| quote_column_name(key) }.join(", ")
25
+
26
+ on_delete = options.delete(:on_delete)
27
+ on_update = options.delete(:on_update)
28
+
29
+ sql = "ALTER TABLE #{quote_table_name(table_name)} "
30
+ sql << "ADD CONSTRAINT #{quote_column_name(name)} "
31
+ sql << "FOREIGN KEY (#{columns}) REFERENCES #{quote_table_name(reference)} (#{keys})"
32
+ sql << " ON DELETE #{on_delete.to_s.gsub(/_/, " ")}" unless on_delete.nil?
33
+ sql << " ON UPDATE #{on_update.to_s.gsub(/_/, " ")}" unless on_update.nil?
34
+
35
+ execute(sql)
36
+ end
37
+
38
+ # Removes a foreign key from the table.
39
+ # Accepts either a column name Symbol/String or an Array of either (will use foreign_key_name method to generate the constraint name).
40
+ # Additionally you can provide the +:name+ option with the FK name.
41
+ #
42
+ # remove_foreign_key :albums, :name => "albums_user_id_fk"
43
+ # remove_foreign_key :albums, :user_id
44
+ def remove_foreign_key(table_name, *options)
45
+ end
46
+
47
+ # Generates a foreign key from given table name and columns
48
+ def foreign_key_name(table_name, columns)
49
+ "#{table_name}_#{Array(columns).map(&:to_s).join("_")}_fk"
50
+ end
51
+
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,43 @@
1
+ module Perfectline
2
+ module ConnectionAdapters
3
+ module MysqlAdapter
4
+
5
+ def self.included(base)
6
+ base.__send__(:include, InstanceMethods)
7
+ end
8
+
9
+ module InstanceMethods
10
+
11
+ def remove_foreign_key(table_name, *args)
12
+ options = args.extract_options!
13
+ name = options.delete(:name) || foreign_key_name(table_name, args.first)
14
+
15
+ execute("ALTER TABLE #{quote_table_name(table_name)} DROP FOREIGN KEY #{quote_column_name(name)}")
16
+ execute("ALTER TABLE #{quote_table_name(table_name)} DROP KEY #{quote_column_name(name)}")
17
+ end
18
+
19
+ def foreign_keys(table_name)
20
+ sql = execute("SHOW CREATE TABLE #{quote_table_name(table_name)}").fetch_hash["Create Table"]
21
+
22
+ returning([]) do |fks|
23
+ sql.scan(/CONSTRAINT `(\w+)` FOREIGN KEY \((.*?)\) REFERENCES `(\w+)` \((.*?)\)(?:\s+ON DELETE\s+(.*?))?(?:\s+ON UPDATE\s+(.*?))?[,\)\n]/) do |name, cols, refs, keys, delete, update|
24
+
25
+ cols = returning([]){ |array| cols.scan(/`(\w+)`/) { |match| array << match.first.to_sym }}
26
+ keys = returning([]){ |array| keys.scan(/`(\w+)`/) { |match| array << match.first.to_sym }}
27
+
28
+ fks.push({
29
+ :name => name,
30
+ :columns => cols,
31
+ :keys => keys,
32
+ :references => refs,
33
+ :on_update => update,
34
+ :on_delete => delete
35
+ })
36
+ end
37
+ end
38
+ end
39
+
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,75 @@
1
+ module Perfectline
2
+ module ConnectionAdapters
3
+ module PostgreSQLAdapter
4
+
5
+ def self.included(base)
6
+ base.__send__(:include, InstanceMethods)
7
+ end
8
+
9
+ module InstanceMethods
10
+
11
+ def remove_foreign_key(table_name, *args)
12
+ options = args.extract_options!
13
+ name = options.delete(:name) || foreign_key_name(table_name, args.first)
14
+
15
+ execute("ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(name)}")
16
+ end
17
+
18
+ def foreign_keys(table_name)
19
+ sql = %Q{
20
+ SELECT t.constraint_name AS name,
21
+ k.table_name AS from_table,
22
+ k.column_name AS from_column,
23
+ c.table_name AS to_table,
24
+ c.column_name AS to_column,
25
+ r.update_rule AS update_rule,
26
+ r.delete_rule AS delete_rule
27
+ FROM
28
+ information_schema.table_constraints t,
29
+ information_schema.constraint_column_usage c,
30
+ information_schema.key_column_usage k,
31
+ information_schema.referential_constraints r
32
+ WHERE
33
+ t.constraint_name = c.constraint_name AND
34
+ k.constraint_name = c.constraint_name AND
35
+ r.constraint_name = c.constraint_name AND
36
+ t.constraint_type = 'FOREIGN KEY' AND
37
+ t.table_name = '#{table_name}'
38
+ }
39
+
40
+ foreign_keys = returning({}) do |foreign_keys|
41
+ select_all(sql).each do |row|
42
+ if foreign_keys.has_key?(row["name"])
43
+ foreign_keys[row["name"]][:columns] << row["from_column"]
44
+ foreign_keys[row["name"]][:keys] << row["to_column"]
45
+ else
46
+ foreign_keys.store(row["name"], {
47
+ :name => row["name"],
48
+ :columns => [row["from_column"]],
49
+ :references => row["to_table"],
50
+ :keys => [row["to_column"]],
51
+ :on_update => row["update_rule"],
52
+ :on_delete => row["delete_rule"]
53
+ })
54
+ end
55
+ end
56
+ end
57
+
58
+ foreign_keys.each_value do |value|
59
+ columns = Array(value[:columns]).uniq.compact
60
+ keys = Array(value[:keys]).uniq.compact
61
+
62
+ value[:columns] = columns.size == 1 ? columns.first : columns
63
+ value[:keys] = keys.size == 1 ? keys.first : keys
64
+
65
+ value.delete(:on_update) if value[:on_update] == "NO ACTION"
66
+ value.delete(:on_delete) if value[:on_delete] == "NO ACTION"
67
+ end
68
+
69
+ return foreign_keys.values.to_a
70
+ end
71
+
72
+ end
73
+ end
74
+ end
75
+ end
@@ -1,33 +1,38 @@
1
- ActiveRecord::SchemaDumper.class_eval do
2
- private
1
+ module Perfectline
2
+ module SchemaDumper
3
3
 
4
- def tables(stream)
5
- table_names = @connection.tables.sort.reject do |tbl|
6
- ['schema_migrations', 'schema_info', ignore_tables].flatten.any? do |ignored|
7
- case ignored
8
- when String; tbl == ignored
9
- when Regexp; tbl =~ ignored
10
- else
11
- raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.'
4
+ def self.included(base)
5
+ base.__send__(:include, InstanceMethods)
6
+ base.__send__(:alias_method_chain, :tables, :foreign_keys)
7
+ end
8
+
9
+ module InstanceMethods
10
+ def tables_with_foreign_keys(stream)
11
+ tables_without_foreign_keys(stream)
12
+ @connection.tables.sort.each do |table|
13
+ foreign_keys(table, stream)
12
14
  end
13
15
  end
14
- end
15
16
 
16
- table_names.each { |tbl| table(tbl, stream) }
17
- table_names.each { |tbl| foreign_keys(tbl, stream) } if @connection.respond_to?(:foreign_keys)
18
- end
17
+ def foreign_keys(table_name, stream)
18
+ @connection.foreign_keys(table_name).each do |foreign_key|
19
+ stream.print " add_foreign_key #{table_name.inspect}, #{foreign_key[:columns].inspect}"
20
+ if foreign_key[:references] && foreign_key[:references] != foreign_key[:columns].first.to_s.gsub(/_id$/, "").tableize
21
+ stream.print ", :references => #{foreign_key[:references].inspect}"
22
+ end
23
+ if foreign_key[:keys] && foreign_key[:keys] != ["id"]
24
+ stream.print ", :keys => #{foreign_key[:keys].inspect}"
25
+ end
26
+ if foreign_key[:name]
27
+ stream.print ", :name => #{foreign_key[:name].inspect}"
28
+ end
19
29
 
20
- def foreign_keys(table, stream)
21
- foreign_keys = @connection.foreign_keys(table).sort { |a, b| a[:columns] <=> b[:columns] }
22
- foreign_keys.each do |foreign_key|
23
- stream.print " add_foreign_key #{table.inspect}, #{foreign_key[:columns].inspect}"
24
- stream.print ", :references => #{foreign_key[:references].inspect}" if foreign_key[:references] && foreign_key[:references] != foreign_key[:columns].first.gsub(/_id$/, '').tableize
25
- stream.print ", :keys => #{foreign_key[:keys].inspect}" if foreign_key[:keys] && foreign_key[:keys] != ["id"]
26
- stream.print ", :name => #{foreign_key[:name].inspect}" if foreign_key[:name]
27
- stream.print ", :on_delete => #{foreign_key[:on_delete].downcase.gsub(/ /, '_').to_sym.inspect}" if foreign_key[:on_delete]
28
- stream.print ", :on_update => #{foreign_key[:on_update].downcase.gsub(/ /, '_').to_sym.inspect}" if foreign_key[:on_update]
29
- stream.puts
30
+ stream.print ", :on_delete => #{foreign_key[:on_delete].downcase.gsub(/ /, '_').to_sym.inspect}" if foreign_key[:on_delete]
31
+ stream.print ", :on_update => #{foreign_key[:on_update].downcase.gsub(/ /, '_').to_sym.inspect}" if foreign_key[:on_update]
32
+ stream.puts
33
+ end
34
+ end
30
35
  end
31
- stream.puts unless foreign_keys.empty?
36
+
32
37
  end
33
- end
38
+ end
data/lib/foreign_keys.rb CHANGED
@@ -1,4 +1,18 @@
1
- require 'foreign_keys/mysql_adapter' if defined?(ActiveRecord::ConnectionAdapters::MysqlAdapter)
2
- require 'foreign_keys/postgresql_adapter' if defined?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
3
- require 'foreign_keys/schema_dumper'
4
- require 'foreign_keys/schema_statements'
1
+ require "foreign_keys/connection_adapters/abstract"
2
+ require "foreign_keys/connection_adapters/mysql"
3
+ require "foreign_keys/connection_adapters/postgresql"
4
+
5
+ require "foreign_keys/schema_dumper"
6
+
7
+
8
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.__send__(:include, Perfectline::ConnectionAdapters::AbstractAdapter)
9
+
10
+ if defined?(ActiveRecord::ConnectionAdapters::MysqlAdapter)
11
+ ActiveRecord::ConnectionAdapters::MysqlAdapter.__send__(:include, Perfectline::ConnectionAdapters::MysqlAdapter)
12
+ end
13
+
14
+ if defined?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
15
+ ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.__send__(:include, Perfectline::ConnectionAdapters::PostgreSQLAdapter)
16
+ end
17
+
18
+ ActiveRecord::SchemaDumper.__send__(:include, Perfectline::SchemaDumper)
metadata CHANGED
@@ -1,20 +1,23 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreign_keys
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
- - Dan Walters
7
+ - Tanel Suurhans
8
+ - Tarmo Lehtpuu
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
12
 
12
- date: 2009-05-26 00:00:00 +03:00
13
+ date: 2009-11-23 00:00:00 +02:00
13
14
  default_executable:
14
15
  dependencies: []
15
16
 
16
- description: Foreign key support plugin for Rails and MySQL, with schema dumper integration
17
+ description: Foreign key support plugin for Rails, with schema dumper integration
17
18
  email:
19
+ - tanel.suurhans@perfectline.ee
20
+ - tarmo.lehtpuu@perfectline.ee
18
21
  executables: []
19
22
 
20
23
  extensions: []
@@ -22,19 +25,15 @@ extensions: []
22
25
  extra_rdoc_files:
23
26
  - README.markdown
24
27
  files:
25
- - .gitignore
26
28
  - README.markdown
27
- - Rakefile
28
- - VERSION.yml
29
- - foreign_keys.gemspec
30
29
  - init.rb
31
30
  - lib/foreign_keys.rb
32
- - lib/foreign_keys/mysql_adapter.rb
33
- - lib/foreign_keys/postgresql_adapter.rb
31
+ - lib/foreign_keys/connection_adapters/abstract.rb
32
+ - lib/foreign_keys/connection_adapters/mysql.rb
33
+ - lib/foreign_keys/connection_adapters/postgresql.rb
34
34
  - lib/foreign_keys/schema_dumper.rb
35
- - lib/foreign_keys/schema_statements.rb
36
35
  has_rdoc: true
37
- homepage: http://github.com/dwalters/foreign_keys/tree/master
36
+ homepage: http://github.com/perfectline/foreign_keys/tree/master
38
37
  licenses: []
39
38
 
40
39
  post_install_message:
@@ -60,6 +59,6 @@ rubyforge_project:
60
59
  rubygems_version: 1.3.5
61
60
  signing_key:
62
61
  specification_version: 3
63
- summary: Foreign key sypport for Rails (MySQL, PostgreSQL)
62
+ summary: Foreign key support for Rails
64
63
  test_files: []
65
64
 
data/.gitignore DELETED
@@ -1,3 +0,0 @@
1
- pkg
2
- rdoc
3
- .idea
data/Rakefile DELETED
@@ -1,28 +0,0 @@
1
- require 'rake'
2
- require 'rake/testtask'
3
- require 'rake/rdoctask'
4
-
5
- begin
6
- require 'jeweler'
7
- Jeweler::Tasks.new do |jewel|
8
- jewel.name = 'foreign_keys'
9
- jewel.summary = 'Foreign key sypport for Rails (MySQL, PostgreSQL)'
10
- jewel.homepage = 'http://github.com/dwalters/foreign_keys/tree/master'
11
- jewel.description = 'Foreign key support plugin for Rails and MySQL, with schema dumper integration'
12
- jewel.authors = ["Dan Walters"]
13
- end
14
- rescue LoadError
15
- puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
16
- end
17
-
18
- desc 'Default: run unit tests.'
19
- task :default => :rdoc
20
-
21
- desc 'Generate documentation for the smurf plugin.'
22
- Rake::RDocTask.new(:rdoc) do |rdoc|
23
- rdoc.rdoc_dir = 'rdoc'
24
- rdoc.title = 'Foreign keys'
25
- rdoc.options << '--line-numbers' << '--inline-source'
26
- rdoc.rdoc_files.include('README.markdown')
27
- rdoc.rdoc_files.include('lib/**/*.rb')
28
- end
data/VERSION.yml DELETED
@@ -1,4 +0,0 @@
1
- ---
2
- :major: 1
3
- :minor: 2
4
- :patch: 0
data/foreign_keys.gemspec DELETED
@@ -1,42 +0,0 @@
1
- # -*- encoding: utf-8 -*-
2
-
3
- Gem::Specification.new do |s|
4
- s.name = %q{foreign_keys}
5
- s.version = "1.2.0"
6
-
7
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
- s.authors = ["Dan Walters"]
9
- s.date = %q{2009-05-26}
10
- s.description = %q{Foreign key support plugin for Rails and MySQL, with schema dumper integration}
11
- s.extra_rdoc_files = [
12
- "README.markdown"
13
- ]
14
- s.files = [
15
- ".gitignore",
16
- "README.markdown",
17
- "Rakefile",
18
- "VERSION.yml",
19
- "foreign_keys.gemspec",
20
- "init.rb",
21
- "lib/foreign_keys.rb",
22
- "lib/foreign_keys/mysql_adapter.rb",
23
- "lib/foreign_keys/postgresql_adapter.rb",
24
- "lib/foreign_keys/schema_dumper.rb",
25
- "lib/foreign_keys/schema_statements.rb"
26
- ]
27
- s.homepage = %q{http://github.com/dwalters/foreign_keys/tree/master}
28
- s.rdoc_options = ["--charset=UTF-8"]
29
- s.require_paths = ["lib"]
30
- s.rubygems_version = %q{1.3.3}
31
- s.summary = %q{Foreign key sypport for Rails (MySQL, PostgreSQL)}
32
-
33
- if s.respond_to? :specification_version then
34
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
35
- s.specification_version = 3
36
-
37
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
38
- else
39
- end
40
- else
41
- end
42
- end
@@ -1,22 +0,0 @@
1
- ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
2
- def foreign_keys(table_name)#:nodoc:
3
- sql = execute("SHOW CREATE TABLE #{quote_table_name(table_name)}").fetch_hash["Create Table"]
4
-
5
- foreign_keys = []
6
- sql.scan(/CONSTRAINT `(\w+)` FOREIGN KEY \((.*?)\) REFERENCES `(\w+)` \((.*?)\)(?:\s+ON DELETE\s+(.*?))?(?:\s+ON UPDATE\s+(.*?))?[,\)\n]/) do |name, column_list, references, key_list, on_delete, on_update|
7
- columns = []; column_list.scan(/`(\w+)`/) { |match| columns << match.first }
8
- keys = []; key_list.scan(/`(\w+)`/) { |match| keys << match.first }
9
-
10
- foreign_keys << {
11
- :name => name,
12
- :columns => columns,
13
- :references => references,
14
- :keys => keys,
15
- :on_update => on_update,
16
- :on_delete => on_delete
17
- }
18
- end
19
-
20
- foreign_keys
21
- end
22
- end
@@ -1,45 +0,0 @@
1
- ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
2
- def foreign_keys(table_name)#:nodoc:
3
- data = select_all(<<-EOF)
4
- select t.constraint_name as name,
5
- k.table_name as from_table,
6
- k.column_name as from_column,
7
- c.table_name as to_table,
8
- c.column_name as to_column,
9
- r.update_rule as update_rule,
10
- r.delete_rule as delete_rule
11
- from information_schema.table_constraints t,
12
- information_schema.constraint_column_usage c,
13
- information_schema.key_column_usage k,
14
- information_schema.referential_constraints r
15
- where t.constraint_name = c.constraint_name and
16
- k.constraint_name = c.constraint_name and
17
- r.constraint_name = c.constraint_name and
18
- t.constraint_type = 'FOREIGN KEY' and
19
- t.table_name = '#{table_name}'
20
- EOF
21
-
22
- foreign_keys = data.inject({}) do |list, row|
23
- if !list[row["name"]]
24
- list[row["name"]] = {
25
- :name => row["name"],
26
- :columns => [row["from_column"]],
27
- :references => row["to_table"],
28
- :keys => [row["to_column"]],
29
- :on_update => row["update_rule"],
30
- :on_delete => row["delete_rule"]
31
- }
32
- else
33
- list[row["name"]][:columns] << row["from_column"]
34
- list[row["name"]][:keys] << row["to_column"]
35
- end
36
- list
37
- end.map do |key, ref|
38
- ref[:columns] = Array(ref[:columns]).uniq.compact.join(", ")
39
- ref[:keys] = Array(ref[:keys]).uniq.compact.join(", ")
40
- ref.delete(:on_update) if ref[:on_update] == "NO ACTION"
41
- ref.delete(:on_delete) if ref[:on_delete] == "NO ACTION"
42
- ref
43
- end
44
- end
45
- end
@@ -1,70 +0,0 @@
1
- ActiveRecord::ConnectionAdapters::SchemaStatements.module_eval do
2
- # Adds a new foreign key to a table. +columns+ can be a single Symbol, or an
3
- # array of Symbols, on which the key will be added.
4
- #
5
- # The referenced table name and columns are deduced using the usual conventions
6
- # unless explicitly specified via +:references+ and/or +:keys+ options.
7
- #
8
- # The foreign key will be named using the same rules as #add_index, unless you
9
- # pass +:name+ as an option.
10
- #
11
- # Options for +:on_delete+ and +:on_update+ may be specified. Acceptable
12
- # values are +:restrict+, +:set_null+, and +:cascade+.
13
- #
14
- # Note that some databases will automatically create an index on the constrained
15
- # columns.
16
- #
17
- # ===== Examples
18
- # ====== Creating a simple foreign key
19
- # add_foreign_key :orders, :user_id
20
- # generates
21
- # ALTER TABLE orders ADD CONSTRAINT index_orders_on_user_id FOREIGN KEY (user_id) REFERENCES users (id)
22
- # ====== Specifying the target table
23
- # add_foreign_key :articles, :author_id, :references => :users
24
- # generates
25
- # ALTER TABLE articles ADD CONSTRAINT index_articles_on_author_id FOREIGN KEY (author_id) REFERENCES users (id)
26
- # ====== Cascading deletes
27
- # add_foreign_key :comments, :post_id, :on_delete => :cascade
28
- # generates
29
- # ALTER TABLE comments ADD CONSTRAINT index_comments_on_post_id FOREIGN KEY (post_id) REFERENCES posts (id) ON DELETE CASCADE
30
- def add_foreign_key(table_name, columns, options = {})
31
- columns = Array(columns)
32
- name = options[:name] || index_name(table_name, :column => columns)
33
- column_list = columns.collect { |column| quote_column_name(column) }.join(", ")
34
- keys = Array(options[:keys] || :id)
35
- key_list = keys.collect { |key| quote_column_name(key) }.join(", ")
36
- references = if options[:references]
37
- options[:references]
38
- else
39
- columns.first.to_s.gsub(/_id$/, '').tableize
40
- end
41
-
42
- sql = "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{quote_column_name(name)} FOREIGN KEY (#{column_list}) REFERENCES #{quote_table_name(references)} (#{key_list})"
43
- sql << " ON DELETE #{options[:on_delete].to_s.gsub(/_/,' ')}" if options[:on_delete]
44
- sql << " ON UPDATE #{options[:on_update].to_s.gsub(/_/,' ')}" if options[:on_update]
45
- execute sql
46
- end
47
-
48
- # Remove the given foreign key from the table. If +:name+ is specified, it will be used
49
- # directly as the name of the key to delete. Otherwise, the same conventions used
50
- # when by #add_foreign_key may be provided.
51
- #
52
- # Any underlying indexes will not be automatically dropped.
53
- #
54
- # ===== Examples
55
- # ====== Removing a foreign key by name
56
- # remove_foreign_key :orders, :name => "index_orders_on_user_id"
57
- # ====== Removing a foreign key by column
58
- # remove_foreign_key :orders, :user_id
59
- def remove_foreign_key(table_name, *args)
60
- options = args.extract_options!
61
- name = if options[:name]
62
- options[:name]
63
- else
64
- columns = args.first
65
- index_name(table_name, :column => Array(columns))
66
- end
67
-
68
- execute "ALTER TABLE #{quote_table_name(table_name)} DROP FOREIGN KEY #{quote_column_name(name)}"
69
- end
70
- end