pg_shrink 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Shrinkfile.example +11 -7
- data/bin/pg_shrink +4 -0
- data/lib/pg_shrink/database/postgres.rb +79 -6
- data/lib/pg_shrink/database.rb +25 -2
- data/lib/pg_shrink/sub_table_filter.rb +24 -0
- data/lib/pg_shrink/sub_table_operator.rb +0 -1
- data/lib/pg_shrink/table.rb +40 -13
- data/lib/pg_shrink/table_filter.rb +27 -5
- data/lib/pg_shrink/version.rb +1 -1
- data/lib/pg_shrink.rb +4 -2
- data/spec/pg_shrink_spec.rb +213 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1f5d381b33119abf32ee3e51c3d9efe5997194ff
|
4
|
+
data.tar.gz: 255bcaed0c21c079370b483ac5941a5c7a320c1f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8c6a6fc900c387196c7dfb973337123867b010bb6e7e9ea85e543e69ff23c0d69a1cfab472133b406d4ee421f8a9caf2e650671ca7cc9fee45b35016dffb8783
|
7
|
+
data.tar.gz: 477d58878e5e00c8bf2f73d2818e4e672d5172eff9533c44d97025fdf1013637a380bda218d0e970575c846606471845538efdd8d62734b343738d8a22115ea2
|
data/Shrinkfile.example
CHANGED
@@ -1,12 +1,16 @@
|
|
1
1
|
filter_table :users do |f|
|
2
2
|
|
3
|
-
# filter_by takes a
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
3
|
+
# filter_by takes a sql condition for the records you would like to keep.
|
4
|
+
# This condition can be as a hash or string
|
5
|
+
f.filter_by("id % 1000 == 0")
|
6
|
+
|
7
|
+
# NOTE: It can also take a block that expects the fields of each record (as
|
8
|
+
# a hash) the block should return true to keep the record, false if not.
|
9
|
+
# However, this approach is substantially slower than the conditions hash
|
10
|
+
# based approach.
|
11
|
+
#f.filter_by do |u|
|
12
|
+
# u[:id] % 1000 == 0
|
13
|
+
#end
|
10
14
|
|
11
15
|
# lock takes a block and yields the fields of each record (as a hash of
|
12
16
|
# fieldname => value) If the block returns true this record is immune to all
|
data/bin/pg_shrink
CHANGED
@@ -37,6 +37,10 @@ Please make sure you have a Shrinkfile or specify one using -c
|
|
37
37
|
options[:force] = true
|
38
38
|
end
|
39
39
|
|
40
|
+
verbose_desc = 'run in verbose mode'
|
41
|
+
opts.on('-v', '--verbose', verbose_desc) do
|
42
|
+
options[:log] = true
|
43
|
+
end
|
40
44
|
|
41
45
|
opts.on('-h', '--help', 'Show this message and exit') do |h|
|
42
46
|
puts opts
|
@@ -27,6 +27,15 @@ module PgShrink
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
30
|
+
def database_name
|
31
|
+
if @opts[:postgres_url]
|
32
|
+
@opts[:postgres_url] =~ /.*\/([^\/]+)$/
|
33
|
+
return $1
|
34
|
+
else
|
35
|
+
@opts[:database]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
30
39
|
def batch_size
|
31
40
|
@opts[:batch_size]
|
32
41
|
end
|
@@ -46,13 +55,18 @@ module PgShrink
|
|
46
55
|
end
|
47
56
|
max_id = self.connection["select max(#{primary_key}) from #{table_name}"].
|
48
57
|
first[:max]
|
49
|
-
i =
|
58
|
+
i = 0;
|
50
59
|
while i < max_id do
|
51
60
|
sql = "select * from #{table_name} where " +
|
52
|
-
"#{primary_key}
|
53
|
-
batch = self.connection[sql].all
|
61
|
+
"#{primary_key} > #{i} limit #{batch_size}"
|
62
|
+
batch = self.connection[sql].all.compact
|
63
|
+
|
54
64
|
yield(batch)
|
55
|
-
|
65
|
+
if batch.any?
|
66
|
+
i = batch.last[primary_key]
|
67
|
+
else
|
68
|
+
break
|
69
|
+
end
|
56
70
|
end
|
57
71
|
end
|
58
72
|
|
@@ -87,8 +101,67 @@ module PgShrink
|
|
87
101
|
self.connection.from(table_name).where(opts).all
|
88
102
|
end
|
89
103
|
|
90
|
-
def delete_records(table_name,
|
91
|
-
|
104
|
+
def delete_records(table_name, conditions, exclude_conditions = [])
|
105
|
+
query = connection.from(table_name)
|
106
|
+
Array.wrap(conditions).compact.each do |cond|
|
107
|
+
query = query.where(cond)
|
108
|
+
end
|
109
|
+
Array.wrap(exclude_conditions).compact.each do |exclude_cond|
|
110
|
+
query = query.exclude(exclude_cond)
|
111
|
+
end
|
112
|
+
query.delete
|
113
|
+
end
|
114
|
+
|
115
|
+
def propagate_delete(opts)
|
116
|
+
# what we conceptually want to do is delete the left outer join where id is null.
|
117
|
+
# That's not working in postgres, so we instead use where not exists. Docs
|
118
|
+
# indicate using where not exists and select 1 in this case.
|
119
|
+
# See:
|
120
|
+
# http://www.postgresql.org/docs/current/interactive/functions-subquery.html#FUNCTIONS-SUBQUERY-EXISTS
|
121
|
+
query = "DELETE FROM #{opts[:child_table]} WHERE NOT EXISTS (" +
|
122
|
+
"SELECT 1 from #{opts[:parent_table]} where " +
|
123
|
+
"#{opts[:child_table]}.#{opts[:child_key]} = " +
|
124
|
+
"#{opts[:parent_table]}.#{opts[:parent_key]}" +
|
125
|
+
")"
|
126
|
+
|
127
|
+
|
128
|
+
# Outside of the join statements, we want to maintain the ease of hash-based
|
129
|
+
# conditions. Do this by using a query builder but then swapping in delete SQL
|
130
|
+
# in the end.
|
131
|
+
query_builder = connection.from(opts[:child_table])
|
132
|
+
Array.wrap(opts[:conditions]).compact.each do |cond|
|
133
|
+
query_builder = query_builder.where(cond)
|
134
|
+
end
|
135
|
+
Array.wrap(opts[:exclude]).compact.each do |exclude_cond|
|
136
|
+
query_builder = query_builder.exclude(exclude_cond)
|
137
|
+
end
|
138
|
+
sql = query_builder.sql.gsub("WHERE", "AND").
|
139
|
+
gsub("SELECT * FROM \"#{opts[:child_table]}\"",
|
140
|
+
query)
|
141
|
+
|
142
|
+
connection[sql].delete
|
143
|
+
end
|
144
|
+
|
145
|
+
def vacuum_and_reindex!(table_name)
|
146
|
+
self.log("Beginning vacuum on #{table_name}")
|
147
|
+
connection["vacuum full #{table_name}"].first
|
148
|
+
self.log("Beginning reindex on #{table_name}")
|
149
|
+
connection["reindex table #{table_name}"].first
|
150
|
+
self.log("done reindexing #{table_name}")
|
151
|
+
end
|
152
|
+
|
153
|
+
def vacuum_and_reindex_all!
|
154
|
+
self.log("Beginning full database vacuum")
|
155
|
+
connection["vacuum full"].first
|
156
|
+
self.log("beginning full database reindex")
|
157
|
+
connection["reindex database #{database_name}"].first
|
158
|
+
self.log("done reindexing full database")
|
159
|
+
end
|
160
|
+
|
161
|
+
def shrink!
|
162
|
+
filter!
|
163
|
+
vacuum_and_reindex_all!
|
164
|
+
sanitize!
|
92
165
|
end
|
93
166
|
end
|
94
167
|
end
|
data/lib/pg_shrink/database.rb
CHANGED
@@ -41,8 +41,21 @@ module PgShrink
|
|
41
41
|
raise "implement in subclass"
|
42
42
|
end
|
43
43
|
|
44
|
-
# The delete_records method takes a table name
|
45
|
-
|
44
|
+
# The delete_records method takes a table name a condition to delete on,
|
45
|
+
# and a condition to prevent deletion on. This can be used to combine
|
46
|
+
# a targeted deletion with exclusions or to delete an entire table but
|
47
|
+
# for some exclusions by passing no conditions but some exclude conditions.
|
48
|
+
def delete_records(table_name, conditions, exclude_conditions = [])
|
49
|
+
raise "implement in subclass"
|
50
|
+
end
|
51
|
+
|
52
|
+
# vacuum and reindex is pg specific... do nothing in other cases
|
53
|
+
def vacuum_and_reindex!(table_name)
|
54
|
+
end
|
55
|
+
|
56
|
+
# This is kind of a leaky abstraction b/c I'm not sure how this would work
|
57
|
+
# outside of sql
|
58
|
+
def propagate_delete(opts)
|
46
59
|
raise "implement in subclass"
|
47
60
|
end
|
48
61
|
|
@@ -58,5 +71,15 @@ module PgShrink
|
|
58
71
|
filter!
|
59
72
|
sanitize!
|
60
73
|
end
|
74
|
+
|
75
|
+
def initialize(opts = {})
|
76
|
+
@opts = opts
|
77
|
+
end
|
78
|
+
|
79
|
+
def log(message)
|
80
|
+
if @opts[:log]
|
81
|
+
puts "#{Time.now}: #{message}"
|
82
|
+
end
|
83
|
+
end
|
61
84
|
end
|
62
85
|
end
|
@@ -1,6 +1,30 @@
|
|
1
1
|
module PgShrink
|
2
2
|
class SubTableFilter < SubTableOperator
|
3
3
|
|
4
|
+
def propagate_table!
|
5
|
+
primary_key = @opts[:primary_key]
|
6
|
+
foreign_key = @opts[:foreign_key]
|
7
|
+
additional_conditions = {}
|
8
|
+
if @opts[:type_key] && @opts[:type]
|
9
|
+
additional_conditions[@opts[:type_key]] = @opts[:type]
|
10
|
+
end
|
11
|
+
self.database.log("Beginning subtable propagation from " +
|
12
|
+
"#{self.parent.table_name} to #{self.table.table_name}")
|
13
|
+
self.database.propagate_delete(:parent_table => self.parent.table_name,
|
14
|
+
:child_table => self.table.table_name,
|
15
|
+
:parent_key => primary_key,
|
16
|
+
:child_key => foreign_key,
|
17
|
+
:conditions => additional_conditions,
|
18
|
+
:exclude => self.table.lock_opts)
|
19
|
+
|
20
|
+
self.database.log("Done with subtable propagation from " +
|
21
|
+
"#{self.parent.table_name} to #{self.table.table_name}")
|
22
|
+
if self.table.subtable_filters.any?
|
23
|
+
self.database.vacuum_and_reindex!(self.table.table_name)
|
24
|
+
self.table.subtable_filters.each(&:propagate_table!)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
4
28
|
def propagate!(old_parent_data, new_parent_data)
|
5
29
|
return if (old_parent_data.empty? && new_parent_data.empty?)
|
6
30
|
old_batch_keys = old_parent_data.map {|record| record[@opts[:primary_key]]}
|
data/lib/pg_shrink/table.rb
CHANGED
@@ -3,7 +3,7 @@ module PgShrink
|
|
3
3
|
attr_accessor :table_name
|
4
4
|
attr_accessor :database
|
5
5
|
attr_accessor :opts
|
6
|
-
attr_reader :filters, :sanitizers, :subtable_filters, :subtable_sanitizers
|
6
|
+
attr_reader :filters, :sanitizers, :subtable_filters, :subtable_sanitizers, :lock_opts
|
7
7
|
# TODO: Figure out, do we need to be able to support tables with no
|
8
8
|
# keys? If so, how should we handle that?
|
9
9
|
def initialize(database, table_name, opts = {})
|
@@ -31,12 +31,27 @@ module PgShrink
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def lock(opts = {}, &block)
|
34
|
-
@
|
34
|
+
@lock_opts = opts
|
35
|
+
if block_given?
|
36
|
+
puts "WARNING: Block-based lock on #{self.table_name} will make things SLOW"
|
37
|
+
@lock_block = block
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def has_lock?
|
42
|
+
(@lock_opts && @lock_opts.any?) || @lock_block
|
43
|
+
end
|
44
|
+
|
45
|
+
def lock_condition_ok?
|
46
|
+
!@lock_block
|
35
47
|
end
|
36
48
|
|
37
49
|
def locked?(record)
|
38
|
-
if @
|
39
|
-
@
|
50
|
+
if @lock_block
|
51
|
+
@lock_block.call(record)
|
52
|
+
elsif @lock_opts && @lock_opts.any?
|
53
|
+
raise "Unimplemented: Condition-based locks with block-based " +
|
54
|
+
"filter on table #{self.table_name}"
|
40
55
|
end
|
41
56
|
end
|
42
57
|
|
@@ -101,6 +116,17 @@ module PgShrink
|
|
101
116
|
end
|
102
117
|
end
|
103
118
|
|
119
|
+
def condition_filter(filter)
|
120
|
+
self.database.log("Beginning filter on #{table_name}")
|
121
|
+
self.database.delete_records(self.table_name, {}, [filter.opts, lock_opts].compact)
|
122
|
+
self.database.log("Done filtering on #{table_name}")
|
123
|
+
# If there aren't any subtables, there isn't much benefit to vacuuming in
|
124
|
+
# the middle, and we'll wait until we're done with all filters
|
125
|
+
if self.subtable_filters.any?
|
126
|
+
self.database.vacuum_and_reindex!(self.table_name)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
104
130
|
def filter_batch(batch, &filter_block)
|
105
131
|
new_set = batch.select do |record|
|
106
132
|
locked?(record) || filter_block.call(record.dup)
|
@@ -111,11 +137,7 @@ module PgShrink
|
|
111
137
|
|
112
138
|
def sanitize_batch(batch, &sanitize_block)
|
113
139
|
new_set = batch.map do |record|
|
114
|
-
|
115
|
-
record.dup
|
116
|
-
else
|
117
|
-
sanitize_block.call(record.dup)
|
118
|
-
end
|
140
|
+
sanitize_block.call(record.dup)
|
119
141
|
end
|
120
142
|
update_records(batch, new_set)
|
121
143
|
sanitize_subtables(batch, new_set)
|
@@ -126,9 +148,14 @@ module PgShrink
|
|
126
148
|
remove!
|
127
149
|
else
|
128
150
|
self.filters.each do |filter|
|
129
|
-
|
130
|
-
self.
|
131
|
-
|
151
|
+
if filter.conditions? && self.lock_condition_ok?
|
152
|
+
self.condition_filter(filter)
|
153
|
+
self.subtable_filters.each(&:propagate_table!)
|
154
|
+
else
|
155
|
+
self.records_in_batches do |batch|
|
156
|
+
self.filter_batch(batch) do |record|
|
157
|
+
filter.apply(record)
|
158
|
+
end
|
132
159
|
end
|
133
160
|
end
|
134
161
|
end
|
@@ -146,7 +173,7 @@ module PgShrink
|
|
146
173
|
end
|
147
174
|
|
148
175
|
def can_just_remove?
|
149
|
-
self.subtable_filters.empty? && self.subtable_sanitizers.empty? &&
|
176
|
+
self.subtable_filters.empty? && self.subtable_sanitizers.empty? && !has_lock?
|
150
177
|
end
|
151
178
|
|
152
179
|
# Mark @remove and add filter so that if we're in the simple case we can
|
@@ -1,14 +1,36 @@
|
|
1
1
|
module PgShrink
|
2
2
|
class TableFilter
|
3
|
-
attr_accessor :table
|
4
|
-
def initialize(table, opts, &block)
|
3
|
+
attr_accessor :table, :opts
|
4
|
+
def initialize(table, opts = nil, &block)
|
5
5
|
self.table = table
|
6
|
-
@opts = opts
|
7
|
-
@block = block
|
6
|
+
@opts = opts
|
7
|
+
@block = block if block_given?
|
8
|
+
end
|
9
|
+
|
10
|
+
def conditions?
|
11
|
+
# use !empty instead of any? because we accept string conditions
|
12
|
+
!@block
|
8
13
|
end
|
9
14
|
|
10
15
|
def apply(hash)
|
11
|
-
@block
|
16
|
+
if @block
|
17
|
+
@block.call(hash)
|
18
|
+
# if we have a straightforwards conditions hash can just do in place comparisons
|
19
|
+
elsif @opts.is_a?(Hash)
|
20
|
+
@opts.each do |k, v|
|
21
|
+
if [Array, Range].include?(v.class)
|
22
|
+
return false unless v.include?(hash[k])
|
23
|
+
elsif [String, Integer, Float].include?(v.class)
|
24
|
+
return false unless hash[k] == v
|
25
|
+
else
|
26
|
+
raise "Unsupported condition type for mixing with block locks: #{v.class}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
return true
|
30
|
+
#TODO: Figure out if this case matters and we want to support it.
|
31
|
+
elsif @opts.is_a?(String)
|
32
|
+
raise "Unsupported: Mixing string conditions with block locks"
|
33
|
+
end
|
12
34
|
end
|
13
35
|
end
|
14
36
|
end
|
data/lib/pg_shrink/version.rb
CHANGED
data/lib/pg_shrink.rb
CHANGED
@@ -19,7 +19,8 @@ module PgShrink
|
|
19
19
|
url: nil,
|
20
20
|
config: 'Shrinkfile',
|
21
21
|
force: false,
|
22
|
-
batch_size: 10000
|
22
|
+
batch_size: 10000,
|
23
|
+
log: false,
|
23
24
|
}
|
24
25
|
end
|
25
26
|
|
@@ -60,7 +61,8 @@ module PgShrink
|
|
60
61
|
end
|
61
62
|
|
62
63
|
database = Database::Postgres.new(:postgres_url => options[:url],
|
63
|
-
:batch_size => batch_size
|
64
|
+
:batch_size => batch_size,
|
65
|
+
:log => options[:log])
|
64
66
|
|
65
67
|
database.instance_eval(File.read(options[:config]), options[:config], 1)
|
66
68
|
|
data/spec/pg_shrink_spec.rb
CHANGED
@@ -53,6 +53,135 @@ describe PgShrink do
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
end
|
56
|
+
describe "with a conditions string" do
|
57
|
+
before(:each) do
|
58
|
+
database.filter_table(:users) do |f|
|
59
|
+
f.filter_by("name like '%test 1%'")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
it "Should not call records in batches" do
|
64
|
+
expect(database).not_to receive(:records_in_batches)
|
65
|
+
database.shrink!
|
66
|
+
end
|
67
|
+
|
68
|
+
it "Should call delete_records once" do
|
69
|
+
expect(database).to receive(:delete_records).once
|
70
|
+
database.shrink!
|
71
|
+
end
|
72
|
+
|
73
|
+
it "Should result in the appropriate records being deleted" do
|
74
|
+
database.shrink!
|
75
|
+
remaining_users = database.connection.from(:users).all
|
76
|
+
# 1 and 10-19
|
77
|
+
expect(remaining_users.size).to eq(11)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "when filtering just with a conditions hash" do
|
82
|
+
before(:each) do
|
83
|
+
database.filter_table(:users) do |f|
|
84
|
+
f.filter_by({
|
85
|
+
:name => ["test 1", "test 2", "test 3", "test 4", "test 5",
|
86
|
+
"test 6", "test 7", "test 8", "test 9", "test 10"]
|
87
|
+
})
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
it "Should not call records in batches" do
|
92
|
+
expect(database).not_to receive(:records_in_batches)
|
93
|
+
database.shrink!
|
94
|
+
end
|
95
|
+
|
96
|
+
it "Should call delete_records once" do
|
97
|
+
expect(database).to receive(:delete_records).once
|
98
|
+
database.shrink!
|
99
|
+
end
|
100
|
+
|
101
|
+
it "Should result in the appropriate records being deleted" do
|
102
|
+
database.shrink!
|
103
|
+
remaining_users = database.connection.from(:users).all
|
104
|
+
expect(remaining_users.size).to eq(10)
|
105
|
+
end
|
106
|
+
|
107
|
+
describe "and a condition hash lock" do
|
108
|
+
before(:each) do
|
109
|
+
database.filter_table(:users) do |f|
|
110
|
+
f.lock(name: "test 11")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
it "Should not call records in batches" do
|
114
|
+
expect(database).not_to receive(:records_in_batches)
|
115
|
+
database.shrink!
|
116
|
+
end
|
117
|
+
|
118
|
+
it "Should call delete_records once" do
|
119
|
+
expect(database).to receive(:delete_records).once
|
120
|
+
database.shrink!
|
121
|
+
end
|
122
|
+
it "Still results in the appropriate records being deleted" do
|
123
|
+
database.shrink!
|
124
|
+
remaining_users = database.connection.from(:users).all
|
125
|
+
expect(remaining_users.size).to eq(11)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
describe "and a condition string lock" do
|
130
|
+
before(:each) do
|
131
|
+
database.filter_table(:users) do |f|
|
132
|
+
f.lock("name = 'test 11'")
|
133
|
+
end
|
134
|
+
end
|
135
|
+
it "Should not call records in batches" do
|
136
|
+
expect(database).not_to receive(:records_in_batches)
|
137
|
+
database.shrink!
|
138
|
+
end
|
139
|
+
|
140
|
+
it "Should call delete_records once" do
|
141
|
+
expect(database).to receive(:delete_records).once
|
142
|
+
database.shrink!
|
143
|
+
end
|
144
|
+
it "Still results in the appropriate records being deleted" do
|
145
|
+
database.shrink!
|
146
|
+
remaining_users = database.connection.from(:users).all
|
147
|
+
expect(remaining_users.size).to eq(11)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe "and a block-based lock" do
|
152
|
+
before(:each) do
|
153
|
+
database.filter_table(:users) do |f|
|
154
|
+
f.lock do |u|
|
155
|
+
u[:name] == "test 11"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
it "falls back to results in batches" do
|
160
|
+
expect(database).to receive(:records_in_batches)
|
161
|
+
database.shrink!
|
162
|
+
end
|
163
|
+
it "Still results in the appropriate records being deleted" do
|
164
|
+
database.shrink!
|
165
|
+
remaining_users = database.connection.from(:users).all
|
166
|
+
expect(remaining_users.size).to eq(11)
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
describe "with a subtable_filter" do
|
172
|
+
before(:each) do
|
173
|
+
database.filter_table(:users) do |f|
|
174
|
+
f.filter_subtable(:user_preferences, :foreign_key => :user_id)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should remove the appropriate subtable records" do
|
179
|
+
database.shrink!
|
180
|
+
remaining_prefs = database.connection.from(:user_preferences).all
|
181
|
+
expect(remaining_prefs.size).to eq(30)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
56
185
|
|
57
186
|
it "Should not run delete if there is nothing filtered" do
|
58
187
|
database.filter_table(:users) do |f|
|
@@ -312,6 +441,90 @@ describe PgShrink do
|
|
312
441
|
end
|
313
442
|
end
|
314
443
|
|
444
|
+
describe "with condition filters" do
|
445
|
+
describe "simple cascade" do
|
446
|
+
before(:each) do
|
447
|
+
database.filter_table(:users) do |f|
|
448
|
+
f.filter_by(:name => "test 1")
|
449
|
+
f.filter_subtable(:preferences, :foreign_key => :context_id,
|
450
|
+
:type_key => :context_type, :type => 'User')
|
451
|
+
end
|
452
|
+
database.filter!
|
453
|
+
end
|
454
|
+
it "will filter prefs with context_type 'User'" do
|
455
|
+
#
|
456
|
+
remaining_user = database.connection.from(:users).first
|
457
|
+
remaining_preferences = database.connection.from(:preferences).
|
458
|
+
where(:context_type => 'User').all
|
459
|
+
expect(remaining_preferences.size).to eq(3)
|
460
|
+
expect(remaining_preferences.map {|u| u[:context_id]}.uniq).
|
461
|
+
to eq([remaining_user[:id]])
|
462
|
+
end
|
463
|
+
|
464
|
+
it "will not filter preferences without context_type user" do
|
465
|
+
remaining_preferences = database.connection.from(:preferences).
|
466
|
+
where(:context_type => 'OtherClass').all
|
467
|
+
expect(remaining_preferences.size).to eq(20)
|
468
|
+
end
|
469
|
+
end
|
470
|
+
describe "an extra layer of polymorphic subtables" do
|
471
|
+
before(:all) do
|
472
|
+
connection = PgShrink::Database::Postgres.new(PgSpecHelper.pg_config).
|
473
|
+
connection
|
474
|
+
PgSpecHelper.create_table(connection, :preference_dependents,
|
475
|
+
{'context_id' => 'integer',
|
476
|
+
'context_type' => 'character varying(256)',
|
477
|
+
'value' => 'character varying(256)'})
|
478
|
+
end
|
479
|
+
|
480
|
+
before(:each) do
|
481
|
+
PgSpecHelper.clear_table(database.connection, :preference_dependents)
|
482
|
+
prefs = database.connection.from(:preferences).all
|
483
|
+
prefs.each do |pref|
|
484
|
+
database.connection.run(
|
485
|
+
"insert into preference_dependents " +
|
486
|
+
"(context_id, context_type, value) " +
|
487
|
+
"values (#{pref[:id]}, 'Preference', 'depvalue#{pref[:id]}')")
|
488
|
+
|
489
|
+
database.connection.run(
|
490
|
+
"insert into preference_dependents " +
|
491
|
+
"(context_id, context_type, value) " +
|
492
|
+
"values (#{pref[:id]}, 'SomeOtherClass', 'fakevalue#{pref[:id]}')")
|
493
|
+
|
494
|
+
end
|
495
|
+
|
496
|
+
database.filter_table(:users) do |f|
|
497
|
+
f.filter_by(:name => "test 1")
|
498
|
+
f.filter_subtable(:preferences, :foreign_key => :context_id,
|
499
|
+
:type_key => :context_type, :type => 'User')
|
500
|
+
end
|
501
|
+
|
502
|
+
database.filter_table(:preferences) do |f|
|
503
|
+
f.filter_subtable(:preference_dependents,
|
504
|
+
:foreign_key => :context_id,
|
505
|
+
:type_key => :context_type,
|
506
|
+
:type => 'Preference')
|
507
|
+
end
|
508
|
+
database.filter!
|
509
|
+
end
|
510
|
+
it "will filter preference dependents associated with preferences" do
|
511
|
+
remaining_preferences = database.connection.from(:preferences).all
|
512
|
+
remaining_dependents = database.connection.
|
513
|
+
from(:preference_dependents).
|
514
|
+
where(:context_type => 'Preference').all
|
515
|
+
|
516
|
+
expect(remaining_dependents.size).to eq(remaining_preferences.size)
|
517
|
+
end
|
518
|
+
|
519
|
+
it "will not filter preference dependents with different type" do
|
520
|
+
other_dependents = database.connection.
|
521
|
+
from(:preference_dependents).
|
522
|
+
where(:context_type => 'SomeOtherClass').all
|
523
|
+
expect(other_dependents.size).to eq(80)
|
524
|
+
end
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
315
528
|
describe "simple two table filtering" do
|
316
529
|
before(:each) do
|
317
530
|
database.filter_table(:users) do |f|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pg_shrink
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Ball
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-06-
|
11
|
+
date: 2014-06-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pg
|