db_leftovers 1.5.0 → 1.6.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/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:
|