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 +35 -44
- data/lib/foreign_keys/connection_adapters/abstract.rb +54 -0
- data/lib/foreign_keys/connection_adapters/mysql.rb +43 -0
- data/lib/foreign_keys/connection_adapters/postgresql.rb +75 -0
- data/lib/foreign_keys/schema_dumper.rb +31 -26
- data/lib/foreign_keys.rb +18 -4
- metadata +12 -13
- data/.gitignore +0 -3
- data/Rakefile +0 -28
- data/VERSION.yml +0 -4
- data/foreign_keys.gemspec +0 -42
- data/lib/foreign_keys/mysql_adapter.rb +0 -22
- data/lib/foreign_keys/postgresql_adapter.rb +0 -45
- data/lib/foreign_keys/schema_statements.rb +0 -70
data/README.markdown
CHANGED
@@ -1,73 +1,64 @@
|
|
1
|
-
Foreign Keys
|
1
|
+
Foreign Keys
|
2
2
|
===================
|
3
3
|
|
4
|
-
|
5
|
-
|
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
|
-
|
10
|
+
As Rails gem (from GemCutter)
|
13
11
|
|
14
|
-
|
12
|
+
sudo gem install foreign_keys
|
15
13
|
|
16
|
-
|
14
|
+
As Rails plugin
|
17
15
|
|
18
|
-
script/plugin install git://github.com/
|
16
|
+
script/plugin install git://github.com/perfectline/foreign_keys.git
|
19
17
|
|
20
|
-
|
18
|
+
Quickstart
|
21
19
|
-------------------
|
22
20
|
|
23
|
-
Just place the
|
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 :
|
23
|
+
add_foreign_key :albums, :user_id
|
34
24
|
|
35
|
-
|
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
|
-
|
38
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
48
|
-
|
36
|
+
remove_foreign_key :albums, :user_id
|
37
|
+
remove_foreign_key :albums, :name => "my_special_fk"
|
49
38
|
|
50
|
-
|
39
|
+
Note: MySQL creates an index on the FK column automatically. Removing a FK will remove that index also.
|
51
40
|
|
52
|
-
|
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
|
-
|
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
|
-
|
67
|
-
|
68
|
-
|
69
|
-
Dan Walters
|
56
|
+
Authors
|
57
|
+
---------------
|
70
58
|
|
71
|
-
|
59
|
+
**Tanel Suurhans** - tanel.suurhans_at_perfectline.ee
|
60
|
+
**Tarmo Lehtpuu** - tarmo.lehtpuu_at_perfectline.ee
|
72
61
|
|
73
|
-
|
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
|
-
|
2
|
-
|
1
|
+
module Perfectline
|
2
|
+
module SchemaDumper
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
36
|
+
|
32
37
|
end
|
33
|
-
end
|
38
|
+
end
|
data/lib/foreign_keys.rb
CHANGED
@@ -1,4 +1,18 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
|
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:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- Tanel Suurhans
|
8
|
+
- Tarmo Lehtpuu
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
12
|
|
12
|
-
date: 2009-
|
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
|
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/
|
33
|
-
- lib/foreign_keys/
|
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/
|
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
|
62
|
+
summary: Foreign key support for Rails
|
64
63
|
test_files: []
|
65
64
|
|
data/.gitignore
DELETED
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
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
|