foreign_keys 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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