db_leftovers 1.5.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
data/Changelog
CHANGED
data/README.md
CHANGED
@@ -79,6 +79,12 @@ Opts is a hash with the following possible keys:
|
|
79
79
|
* `:set_null` Indicates that the foreign key should be set to null if the referenced row is deleted.
|
80
80
|
* `:cascade` Indicates that the referencing row should be deleted if the referenced row is deleted.
|
81
81
|
|
82
|
+
* `:deferrable` Marks the constraint as deferrable. Accepts these values:
|
83
|
+
|
84
|
+
* `nil` Indicates the constraint is not deferrable (the default).
|
85
|
+
* `:immediate` Indicates the constraint is usually enforced immediately but can be deferred.
|
86
|
+
* `:deferred` Indicates the constraint is always enforced deferred.
|
87
|
+
|
82
88
|
#### Examples
|
83
89
|
|
84
90
|
foreign_key :books, :author_id, :authors, :id
|
@@ -89,7 +95,7 @@ With implicit column names:
|
|
89
95
|
foreign_key :books, :authors
|
90
96
|
foreign_key :books, :authors, :on_delete => :cascade
|
91
97
|
foreign_key :books, :co_author_id, :authors
|
92
|
-
foreign_key :books, :co_author_id, :authors, :on_delete => :cascade
|
98
|
+
foreign_key :books, :co_author_id, :authors, :on_delete => :cascade, :deferred => :immediate
|
93
99
|
|
94
100
|
### check(on\_table, constraint\_name, expression)
|
95
101
|
|
@@ -1,19 +1,21 @@
|
|
1
1
|
module DBLeftovers
|
2
2
|
|
3
3
|
class ForeignKey
|
4
|
-
attr_accessor :constraint_name, :from_table, :from_column, :to_table, :to_column, :set_null, :cascade
|
4
|
+
attr_accessor :constraint_name, :from_table, :from_column, :to_table, :to_column, :set_null, :cascade, :deferrable_initially_immediate, :deferrable_initially_deferred
|
5
5
|
|
6
6
|
def initialize(from_table, from_column, to_table, to_column, opts={})
|
7
7
|
opts = {
|
8
|
+
:deferrable => nil,
|
8
9
|
:on_delete => nil,
|
9
10
|
:name => name_constraint(from_table, from_column)
|
10
11
|
}.merge(opts)
|
11
12
|
opts.keys.each do |k|
|
12
13
|
raise "`:set_null => true` should now be `:on_delete => :set_null`" if k.to_s == 'set_null'
|
13
14
|
raise "`:cascade => true` should now be `:on_delete => :cascade`" if k.to_s == 'cascade'
|
14
|
-
raise "Unknown option: #{k}" unless [:on_delete, :name].include?(k)
|
15
|
+
raise "Unknown option: #{k}" unless [:on_delete, :name, :deferrable].include?(k)
|
15
16
|
end
|
16
17
|
raise "Unknown on_delete option: #{opts[:on_delete]}" unless [nil, :set_null, :cascade].include?(opts[:on_delete])
|
18
|
+
raise "Unknown deferrable option: #{opts[:deferrable]}" unless [nil, :immediate, :deferred].include?(opts[:deferrable])
|
17
19
|
@constraint_name = opts[:name].to_s
|
18
20
|
@from_table = from_table.to_s
|
19
21
|
@from_column = from_column.to_s
|
@@ -21,9 +23,12 @@ module DBLeftovers
|
|
21
23
|
@to_column = to_column.to_s
|
22
24
|
|
23
25
|
@set_null = opts[:on_delete] == :set_null
|
24
|
-
@cascade
|
26
|
+
@cascade = opts[:on_delete] == :cascade
|
27
|
+
@deferrable_initially_immediate = opts[:deferrable] == :immediate
|
28
|
+
@deferrable_initially_deferred = opts[:deferrable] == :deferred
|
25
29
|
|
26
30
|
raise "ON DELETE can't be both set_null and cascade" if @set_null and @cascade
|
31
|
+
raise "DEFERRABLE can't be both immediate and deferred" if @deferrable_initially_immediate and @deferrable_initially_deferred
|
27
32
|
end
|
28
33
|
|
29
34
|
def equals(other)
|
@@ -33,11 +38,24 @@ module DBLeftovers
|
|
33
38
|
other.to_table == to_table and
|
34
39
|
other.to_column == to_column and
|
35
40
|
other.set_null == set_null and
|
36
|
-
other.cascade == cascade
|
41
|
+
other.cascade == cascade and
|
42
|
+
other.deferrable_initially_immediate == deferrable_initially_immediate and
|
43
|
+
other.deferrable_initially_deferred == deferrable_initially_deferred
|
37
44
|
end
|
38
45
|
|
39
46
|
def to_s
|
40
|
-
|
47
|
+
[
|
48
|
+
"<#{@constraint_name}: from #{@from_table}.#{@from_column} to #{@to_table}.#{@to_column}",
|
49
|
+
if @set_null; "ON DELETE SET NULL"
|
50
|
+
elsif @cascade; "ON DELETE CASCADE"
|
51
|
+
else; nil
|
52
|
+
end,
|
53
|
+
if @deferrable_initially_immediate; "DEFERRABLE INITIALLY IMMEDIATE"
|
54
|
+
elsif @deferrable_initially_deferred; "DEFERRABLE INITIALLY DEFERRED"
|
55
|
+
else; nil
|
56
|
+
end,
|
57
|
+
">"
|
58
|
+
].compact.join(" ")
|
41
59
|
end
|
42
60
|
|
43
61
|
def name_constraint(from_table, from_column)
|
@@ -37,13 +37,16 @@ module DBLeftovers
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def execute_add_foreign_key(fk)
|
40
|
-
on_delete = "ON DELETE CASCADE"
|
40
|
+
on_delete = "ON DELETE CASCADE" if fk.cascade
|
41
41
|
on_delete = "ON DELETE SET NULL" if fk.set_null
|
42
|
+
deferrable = "DEFERRABLE INITIALLY DEFERRED" if fk.deferrable_initially_deferred
|
43
|
+
deferrable = "DEFERRABLE INITIALLY IMMEDIATE" if fk.deferrable_initially_immediate
|
42
44
|
execute_sql %{ALTER TABLE #{fk.from_table}
|
43
45
|
ADD CONSTRAINT #{fk.constraint_name}
|
44
46
|
FOREIGN KEY (#{fk.from_column})
|
45
47
|
REFERENCES #{fk.to_table} (#{fk.to_column})
|
46
|
-
#{on_delete}
|
48
|
+
#{on_delete}
|
49
|
+
#{deferrable}}
|
47
50
|
end
|
48
51
|
|
49
52
|
def execute_drop_foreign_key(constraint_name, from_table, from_column)
|
@@ -70,7 +70,9 @@ module DBLeftovers
|
|
70
70
|
a1.attname AS from_column,
|
71
71
|
t2.relname AS to_table,
|
72
72
|
a2.attname AS to_column,
|
73
|
-
c.confdeltype
|
73
|
+
c.confdeltype,
|
74
|
+
c.condeferrable AS deferrable,
|
75
|
+
c.condeferred AS deferred
|
74
76
|
FROM pg_catalog.pg_constraint c,
|
75
77
|
pg_catalog.pg_class t1,
|
76
78
|
pg_catalog.pg_class t2,
|
@@ -94,14 +96,20 @@ module DBLeftovers
|
|
94
96
|
AND pg_catalog.pg_table_is_visible(t1.oid)
|
95
97
|
AND pg_catalog.pg_table_is_visible(t2.oid)
|
96
98
|
EOQ
|
97
|
-
@conn.select_rows(sql).each do |constr_name, from_table, from_column, to_table, to_column, del_type|
|
99
|
+
@conn.select_rows(sql).each do |constr_name, from_table, from_column, to_table, to_column, del_type, deferrable, deferred|
|
98
100
|
del_type = case del_type
|
99
101
|
when 'a'; nil
|
100
102
|
when 'c'; :cascade
|
101
103
|
when 'n'; :set_null
|
102
104
|
else; raise "Unknown del type: #{del_type}"
|
103
105
|
end
|
104
|
-
|
106
|
+
deferrable = deferrable == 't'
|
107
|
+
deferred = deferred == 't'
|
108
|
+
defer_type = if deferrable and deferred; :deferred
|
109
|
+
elsif deferrable; :immediate
|
110
|
+
else; nil
|
111
|
+
end
|
112
|
+
ret[constr_name] = ForeignKey.new(from_table, from_column, to_table, to_column, :name => constr_name, :on_delete => del_type, :deferrable => defer_type)
|
105
113
|
end
|
106
114
|
return ret
|
107
115
|
end
|
data/lib/db_leftovers/version.rb
CHANGED
data/spec/db_leftovers_spec.rb
CHANGED
@@ -72,9 +72,9 @@ describe DBLeftovers do
|
|
72
72
|
it "should create foreign keys on an empty database" do
|
73
73
|
@db.starts_with
|
74
74
|
DBLeftovers::Definition.define :db_interface => @db do
|
75
|
-
foreign_key :books, :shelf_id, :shelves
|
75
|
+
foreign_key :books, :shelf_id, :shelves, :deferrable => :deferred
|
76
76
|
foreign_key :books, :publisher_id, :publishers, :id, :on_delete => :set_null
|
77
|
-
foreign_key :books, :author_id, :authors, :id, :on_delete => :cascade
|
77
|
+
foreign_key :books, :author_id, :authors, :id, :on_delete => :cascade, :deferrable => :immediate
|
78
78
|
end
|
79
79
|
@db.sqls.should have(3).items
|
80
80
|
@db.should have_seen_sql <<-EOQ
|
@@ -82,6 +82,7 @@ describe DBLeftovers do
|
|
82
82
|
ADD CONSTRAINT fk_books_shelf_id
|
83
83
|
FOREIGN KEY (shelf_id)
|
84
84
|
REFERENCES shelves (id)
|
85
|
+
DEFERRABLE INITIALLY DEFERRED
|
85
86
|
EOQ
|
86
87
|
@db.should have_seen_sql <<-EOQ
|
87
88
|
ALTER TABLE books
|
@@ -96,6 +97,7 @@ describe DBLeftovers do
|
|
96
97
|
FOREIGN KEY (author_id)
|
97
98
|
REFERENCES authors (id)
|
98
99
|
ON DELETE CASCADE
|
100
|
+
DEFERRABLE INITIALLY IMMEDIATE
|
99
101
|
EOQ
|
100
102
|
end
|
101
103
|
|
@@ -105,9 +107,9 @@ describe DBLeftovers do
|
|
105
107
|
@db.starts_with
|
106
108
|
DBLeftovers::Definition.define :db_interface => @db do
|
107
109
|
table :books do
|
108
|
-
foreign_key :shelf_id, :shelves
|
110
|
+
foreign_key :shelf_id, :shelves, :deferrable => :deferred
|
109
111
|
foreign_key :publisher_id, :publishers, :id, :on_delete => :set_null
|
110
|
-
foreign_key :author_id, :authors, :id, :on_delete => :cascade
|
112
|
+
foreign_key :author_id, :authors, :id, :on_delete => :cascade, :deferrable => :immediate
|
111
113
|
end
|
112
114
|
end
|
113
115
|
@db.sqls.should have(3).items
|
@@ -116,6 +118,7 @@ describe DBLeftovers do
|
|
116
118
|
ADD CONSTRAINT fk_books_shelf_id
|
117
119
|
FOREIGN KEY (shelf_id)
|
118
120
|
REFERENCES shelves (id)
|
121
|
+
DEFERRABLE INITIALLY DEFERRED
|
119
122
|
EOQ
|
120
123
|
@db.should have_seen_sql <<-EOQ
|
121
124
|
ALTER TABLE books
|
@@ -130,6 +133,7 @@ describe DBLeftovers do
|
|
130
133
|
FOREIGN KEY (author_id)
|
131
134
|
REFERENCES authors (id)
|
132
135
|
ON DELETE CASCADE
|
136
|
+
DEFERRABLE INITIALLY IMMEDIATE
|
133
137
|
EOQ
|
134
138
|
end
|
135
139
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: db_leftovers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -112,7 +112,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
112
112
|
version: '0'
|
113
113
|
segments:
|
114
114
|
- 0
|
115
|
-
hash:
|
115
|
+
hash: 3317985398985958458
|
116
116
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
117
117
|
none: false
|
118
118
|
requirements:
|