sequel_postgresql_triggers 1.0.8 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/MIT-LICENSE +1 -1
- data/README +10 -2
- data/lib/sequel_postgresql_triggers.rb +83 -0
- data/spec/sequel_postgresql_triggers_spec.rb +170 -79
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c908ae3d087271626842952b60bc22365793a3d9
|
4
|
+
data.tar.gz: 1cf7c4c70437c36ab0852d566d600756314c90d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6f10299a3e24a54147217af11e85317484b02b43a0f9faba1120f5f1bfdd402b8f281c4b403f0f785175c94196d6079a9719667965383102e5732f6e0ca1c9b9
|
7
|
+
data.tar.gz: 06f7baa57db107b2fec9d169ebe03f87b737f611adbc302e8af3f0dda2695d0c96d74dd68b50be4b1c7cb0715c4f40b1b17318f41bdf1ed2755defdf94e70ea2
|
data/MIT-LICENSE
CHANGED
data/README
CHANGED
@@ -37,13 +37,21 @@ differs that upon update, the column is also set to CURRENT_TIMESTAMP.
|
|
37
37
|
This takes quite a few arguments (see the RDoc) and sets up a
|
38
38
|
counter cache so that when the counted table is inserted to
|
39
39
|
or deleted from, records in the main table are updated with the
|
40
|
-
count of the corresponding records in the counted table.
|
40
|
+
count of the corresponding records in the counted table. The counter
|
41
|
+
cache column must have a default of 0 for this to work correctly.
|
41
42
|
|
42
43
|
=== Sum Cache - pgt_sum_cache
|
43
44
|
|
44
45
|
Similar to pgt_counter_cache, except instead of storing a count
|
45
46
|
of records in the main table, it stores the sum on one of the
|
46
|
-
columns in summed table.
|
47
|
+
columns in summed table. The sum cache column must have a default
|
48
|
+
of 0 for this to work correctly.
|
49
|
+
|
50
|
+
=== Sum Through Many Cache - pgt_sum_through_many_cache
|
51
|
+
|
52
|
+
Similar to pgt_sum_cache, except instead of a one-to-many relationship,
|
53
|
+
it supports a many-to-many relationship with a single join table. The
|
54
|
+
sum cache column must have a default of 0 for this to work correctly.
|
47
55
|
|
48
56
|
=== Immutable Columns - pgt_immutable
|
49
57
|
|
@@ -137,6 +137,89 @@ module Sequel
|
|
137
137
|
SQL
|
138
138
|
end
|
139
139
|
|
140
|
+
# Turns a column in the main table into a sum cache through a join table.
|
141
|
+
# A sum cache is a column in the main table with the sum of a column in the
|
142
|
+
# summed table for the matching id. The join table must have NOT NULL constraints
|
143
|
+
# on the foreign keys to the main table and summed table and a
|
144
|
+
# composite unique constraint on both foreign keys.
|
145
|
+
#
|
146
|
+
# Arguments:
|
147
|
+
# * opts : option hash, see module documentation, and below.
|
148
|
+
# * :main_table: name of table holding sum cache column
|
149
|
+
# * :main_table_id_column: primary key column in main table referenced by main_table_fk_column (default: :id)
|
150
|
+
# * :sum_column: column in main table containing the sum cache, must be NOT NULL and default to 0
|
151
|
+
# * :summed_table: name of table being summed
|
152
|
+
# * :summed_table_id_column: primary key column in summed_table referenced by summed_table_fk_column (default: ;id)
|
153
|
+
# * :summed_column: column in summed_table being summed, must be NOT NULL
|
154
|
+
# * :join_table: name of table which joins main_table with summed_table
|
155
|
+
# * :main_table_fk_column: column in join_table referencing main_table_id_column, must be NOT NULL
|
156
|
+
# * :summed_table_fk_column: column in join_table referencing summed_table_id_column, must be NOT NULL
|
157
|
+
def pgt_sum_through_many_cache(opts={})
|
158
|
+
main_table = opts.fetch(:main_table)
|
159
|
+
main_table_id_column = opts.fetch(:main_table_id_column, :id)
|
160
|
+
sum_column = opts.fetch(:sum_column)
|
161
|
+
summed_table = opts.fetch(:summed_table)
|
162
|
+
summed_table_id_column = opts.fetch(:summed_table_id_column, :id)
|
163
|
+
summed_column = opts.fetch(:summed_column)
|
164
|
+
join_table = opts.fetch(:join_table)
|
165
|
+
main_table_fk_column = opts.fetch(:main_table_fk_column)
|
166
|
+
summed_table_fk_column = opts.fetch(:summed_table_fk_column)
|
167
|
+
|
168
|
+
trigger_name = opts[:trigger_name] || "pgt_stmc_#{main_table}__#{main_table_id_column}__#{sum_column}__#{summed_table_id_column}__#{summed_column}__#{main_table_fk_column}__#{summed_table_fk_column}"
|
169
|
+
function_name = opts[:function_name] || "pgt_stmc_#{main_table}__#{main_table_id_column}__#{sum_column}__#{summed_table}__#{summed_table_id_column}__#{summed_column}__#{join_table}__#{main_table_fk_column}__#{summed_table_fk_column}"
|
170
|
+
join_trigger_name = opts[:join_trigger_name] || "pgt_stmc_join_#{main_table}__#{main_table_id_column}__#{sum_column}__#{summed_table_id_column}__#{summed_column}__#{main_table_fk_column}__#{summed_table_fk_column}"
|
171
|
+
join_function_name = opts[:join_function_name] || "pgt_stmc_join_#{main_table}__#{main_table_id_column}__#{sum_column}__#{summed_table}__#{summed_table_id_column}__#{summed_column}__#{join_table}__#{main_table_fk_column}__#{summed_table_fk_column}"
|
172
|
+
|
173
|
+
orig_summed_table = summed_table
|
174
|
+
orig_join_table = join_table
|
175
|
+
|
176
|
+
main_table = quote_schema_table(main_table)
|
177
|
+
main_table_id_column = quote_schema_table(main_table_id_column)
|
178
|
+
sum_column = quote_schema_table(sum_column)
|
179
|
+
summed_table = quote_schema_table(summed_table)
|
180
|
+
summed_table_id_column = quote_schema_table(summed_table_id_column)
|
181
|
+
summed_column = quote_schema_table(summed_column)
|
182
|
+
join_table = quote_schema_table(join_table)
|
183
|
+
main_table_fk_column = quote_schema_table(main_table_fk_column)
|
184
|
+
summed_table_fk_column = quote_schema_table(summed_table_fk_column)
|
185
|
+
|
186
|
+
pgt_trigger(orig_summed_table, trigger_name, function_name, [:insert, :delete, :update], <<-SQL)
|
187
|
+
BEGIN
|
188
|
+
IF (TG_OP = 'UPDATE' AND NEW.#{summed_table_id_column} = OLD.#{summed_table_id_column}) THEN
|
189
|
+
UPDATE #{main_table} SET #{sum_column} = #{sum_column} + NEW.#{summed_column} - OLD.#{summed_column} WHERE #{main_table_id_column} IN (SELECT #{main_table_fk_column} FROM #{join_table} WHERE #{summed_table_fk_column} = NEW.#{summed_table_id_column});
|
190
|
+
ELSE
|
191
|
+
IF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
|
192
|
+
UPDATE #{main_table} SET #{sum_column} = #{sum_column} + NEW.#{summed_column} WHERE #{main_table_id_column} IN (SELECT #{main_table_fk_column} FROM #{join_table} WHERE #{summed_table_fk_column} = NEW.#{summed_table_id_column});
|
193
|
+
END IF;
|
194
|
+
IF (TG_OP = 'DELETE' OR TG_OP = 'UPDATE') THEN
|
195
|
+
UPDATE #{main_table} SET #{sum_column} = #{sum_column} - OLD.#{summed_column} WHERE #{main_table_id_column} IN (SELECT #{main_table_fk_column} FROM #{join_table} WHERE #{summed_table_fk_column} = OLD.#{summed_table_id_column});
|
196
|
+
END IF;
|
197
|
+
END IF;
|
198
|
+
IF (TG_OP = 'DELETE') THEN
|
199
|
+
RETURN OLD;
|
200
|
+
END IF;
|
201
|
+
RETURN NEW;
|
202
|
+
END;
|
203
|
+
SQL
|
204
|
+
|
205
|
+
pgt_trigger(orig_join_table, join_trigger_name, join_function_name, [:insert, :delete, :update], <<-SQL)
|
206
|
+
BEGIN
|
207
|
+
IF (NOT (TG_OP = 'UPDATE' AND NEW.#{main_table_fk_column} = OLD.#{main_table_fk_column} AND NEW.#{summed_table_fk_column} = OLD.#{summed_table_fk_column})) THEN
|
208
|
+
IF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
|
209
|
+
UPDATE #{main_table} SET #{sum_column} = #{sum_column} + (SELECT #{summed_column} FROM #{summed_table} WHERE #{summed_table_id_column} = NEW.#{summed_table_fk_column}) WHERE #{main_table_id_column} = NEW.#{main_table_fk_column};
|
210
|
+
END IF;
|
211
|
+
IF (TG_OP = 'DELETE' OR TG_OP = 'UPDATE') THEN
|
212
|
+
UPDATE #{main_table} SET #{sum_column} = #{sum_column} - (SELECT #{summed_column} FROM #{summed_table} WHERE #{summed_table_id_column} = OLD.#{summed_table_fk_column}) WHERE #{main_table_id_column} = OLD.#{main_table_fk_column};
|
213
|
+
END IF;
|
214
|
+
END IF;
|
215
|
+
IF (TG_OP = 'DELETE') THEN
|
216
|
+
RETURN OLD;
|
217
|
+
END IF;
|
218
|
+
RETURN NEW;
|
219
|
+
END;
|
220
|
+
SQL
|
221
|
+
end
|
222
|
+
|
140
223
|
# When rows in a table are updated, touches a timestamp of related rows
|
141
224
|
# in another table.
|
142
225
|
# Arguments:
|
@@ -1,6 +1,8 @@
|
|
1
|
-
#!/usr/bin/env
|
1
|
+
#!/usr/bin/env ruby
|
2
2
|
require 'rubygems'
|
3
3
|
require 'sequel'
|
4
|
+
require 'minitest/spec'
|
5
|
+
require 'minitest/autorun'
|
4
6
|
|
5
7
|
DB = Sequel.connect(ENV['PGT_SPEC_DB']||'postgres:///spgt_test?user=postgres')
|
6
8
|
|
@@ -26,7 +28,7 @@ describe "PostgreSQL Triggers" do
|
|
26
28
|
DB.drop_language(:plpgsql, :cascade=>true) if DB.server_version < 90000
|
27
29
|
end
|
28
30
|
|
29
|
-
|
31
|
+
describe "PostgreSQL Counter Cache Trigger" do
|
30
32
|
before do
|
31
33
|
DB.create_table(:accounts){integer :id; integer :num_entries, :default=>0}
|
32
34
|
DB.create_table(:entries){integer :id; integer :account_id}
|
@@ -39,45 +41,45 @@ describe "PostgreSQL Triggers" do
|
|
39
41
|
DB.drop_table(:entries, :accounts)
|
40
42
|
end
|
41
43
|
|
42
|
-
|
43
|
-
DB[:accounts].order(:id).select_map(:num_entries).
|
44
|
+
it "Should modify counter cache when adding or removing records" do
|
45
|
+
DB[:accounts].order(:id).select_map(:num_entries).must_equal [0, 0]
|
44
46
|
|
45
47
|
DB[:entries] << {:id=>1, :account_id=>1}
|
46
|
-
DB[:accounts].order(:id).select_map(:num_entries).
|
48
|
+
DB[:accounts].order(:id).select_map(:num_entries).must_equal [1, 0]
|
47
49
|
|
48
50
|
DB[:entries] << {:id=>2, :account_id=>1}
|
49
|
-
DB[:accounts].order(:id).select_map(:num_entries).
|
51
|
+
DB[:accounts].order(:id).select_map(:num_entries).must_equal [2, 0]
|
50
52
|
|
51
53
|
DB[:entries] << {:id=>3, :account_id=>nil}
|
52
|
-
DB[:accounts].order(:id).select_map(:num_entries).
|
54
|
+
DB[:accounts].order(:id).select_map(:num_entries).must_equal [2, 0]
|
53
55
|
|
54
56
|
DB[:entries].where(:id=>3).update(:account_id=>2)
|
55
|
-
DB[:accounts].order(:id).select_map(:num_entries).
|
57
|
+
DB[:accounts].order(:id).select_map(:num_entries).must_equal [2, 1]
|
56
58
|
|
57
59
|
DB[:entries].where(:id=>2).update(:account_id=>2)
|
58
|
-
DB[:accounts].order(:id).select_map(:num_entries).
|
60
|
+
DB[:accounts].order(:id).select_map(:num_entries).must_equal [1, 2]
|
59
61
|
|
60
62
|
DB[:entries].where(:id=>2).update(:account_id=>nil)
|
61
|
-
DB[:accounts].order(:id).select_map(:num_entries).
|
63
|
+
DB[:accounts].order(:id).select_map(:num_entries).must_equal [1, 1]
|
62
64
|
|
63
65
|
DB[:entries].where(:id=>2).update(:id=>4)
|
64
|
-
DB[:accounts].order(:id).select_map(:num_entries).
|
66
|
+
DB[:accounts].order(:id).select_map(:num_entries).must_equal [1, 1]
|
65
67
|
|
66
68
|
DB[:entries].where(:id=>4).update(:account_id=>2)
|
67
|
-
DB[:accounts].order(:id).select_map(:num_entries).
|
69
|
+
DB[:accounts].order(:id).select_map(:num_entries).must_equal [1, 2]
|
68
70
|
|
69
71
|
DB[:entries].where(:id=>4).update(:account_id=>nil)
|
70
|
-
DB[:accounts].order(:id).select_map(:num_entries).
|
72
|
+
DB[:accounts].order(:id).select_map(:num_entries).must_equal [1, 1]
|
71
73
|
|
72
74
|
DB[:entries].filter(:id=>4).delete
|
73
|
-
DB[:accounts].order(:id).select_map(:num_entries).
|
75
|
+
DB[:accounts].order(:id).select_map(:num_entries).must_equal [1, 1]
|
74
76
|
|
75
77
|
DB[:entries].delete
|
76
|
-
DB[:accounts].order(:id).select_map(:num_entries).
|
78
|
+
DB[:accounts].order(:id).select_map(:num_entries).must_equal [0, 0]
|
77
79
|
end
|
78
80
|
end
|
79
81
|
|
80
|
-
|
82
|
+
describe "PostgreSQL Created At Trigger" do
|
81
83
|
before do
|
82
84
|
DB.create_table(:accounts){integer :id; timestamp :added_on}
|
83
85
|
DB.pgt_created_at(:accounts, :added_on)
|
@@ -87,21 +89,21 @@ describe "PostgreSQL Triggers" do
|
|
87
89
|
DB.drop_table(:accounts)
|
88
90
|
end
|
89
91
|
|
90
|
-
|
92
|
+
it "Should set the column upon insertion and ignore modifications afterward" do
|
91
93
|
DB[:accounts] << {:id=>1}
|
92
94
|
t = DB[:accounts].get(:added_on)
|
93
|
-
t.strftime('%F').
|
95
|
+
t.strftime('%F').must_equal Date.today.strftime('%F')
|
94
96
|
DB[:accounts].update(:added_on=>Date.today - 60)
|
95
|
-
DB[:accounts].get(:added_on).
|
97
|
+
DB[:accounts].get(:added_on).must_equal t
|
96
98
|
DB[:accounts] << {:id=>2}
|
97
99
|
ds = DB[:accounts].select(:added_on)
|
98
|
-
DB[:accounts].select((Sequel::SQL::NumericExpression.new(:NOOP, ds.filter(:id=>2)) > ds.filter(:id=>1)).as(:x)).first[:x].
|
100
|
+
DB[:accounts].select((Sequel::SQL::NumericExpression.new(:NOOP, ds.filter(:id=>2)) > ds.filter(:id=>1)).as(:x)).first[:x].must_equal true
|
99
101
|
DB[:accounts].filter(:id=>1).update(:id=>3)
|
100
|
-
DB[:accounts].select((Sequel::SQL::NumericExpression.new(:NOOP, ds.filter(:id=>2)) > ds.filter(:id=>3)).as(:x)).first[:x].
|
102
|
+
DB[:accounts].select((Sequel::SQL::NumericExpression.new(:NOOP, ds.filter(:id=>2)) > ds.filter(:id=>3)).as(:x)).first[:x].must_equal true
|
101
103
|
end
|
102
104
|
end
|
103
105
|
|
104
|
-
|
106
|
+
describe "PostgreSQL Immutable Trigger" do
|
105
107
|
before do
|
106
108
|
DB.create_table(:accounts){integer :id; integer :balance, :default=>0}
|
107
109
|
DB.pgt_immutable(:accounts, :balance)
|
@@ -112,29 +114,29 @@ describe "PostgreSQL Triggers" do
|
|
112
114
|
DB.drop_table(:accounts)
|
113
115
|
end
|
114
116
|
|
115
|
-
|
116
|
-
|
117
|
+
it "Should allow modifying columns not marked as immutable" do
|
118
|
+
DB[:accounts].update(:id=>2)
|
117
119
|
end
|
118
120
|
|
119
|
-
|
120
|
-
|
121
|
-
|
121
|
+
it "Should allow updating a column to its existing value" do
|
122
|
+
DB[:accounts].update(:balance=>0)
|
123
|
+
DB[:accounts].update(:balance=>Sequel.*(:balance, :balance))
|
122
124
|
end
|
123
125
|
|
124
|
-
|
125
|
-
proc{DB[:accounts].update(:balance=>1)}.
|
126
|
+
it "Should not allow modifying a column's value" do
|
127
|
+
proc{DB[:accounts].update(:balance=>1)}.must_raise(Sequel::DatabaseError)
|
126
128
|
end
|
127
129
|
|
128
|
-
|
129
|
-
proc{DB[:accounts].update(:balance=>nil)}.
|
130
|
+
it "Should handle NULL values correctly" do
|
131
|
+
proc{DB[:accounts].update(:balance=>nil)}.must_raise(Sequel::DatabaseError)
|
130
132
|
DB[:accounts].delete
|
131
133
|
DB[:accounts] << {:id=>1, :balance=>nil}
|
132
|
-
|
133
|
-
proc{DB[:accounts].update(:balance=>0)}.
|
134
|
+
DB[:accounts].update(:balance=>nil)
|
135
|
+
proc{DB[:accounts].update(:balance=>0)}.must_raise(Sequel::DatabaseError)
|
134
136
|
end
|
135
137
|
end
|
136
138
|
|
137
|
-
|
139
|
+
describe "PostgreSQL Sum Cache Trigger" do
|
138
140
|
before do
|
139
141
|
DB.create_table(:accounts){integer :id; integer :balance, :default=>0}
|
140
142
|
DB.create_table(:entries){integer :id; integer :account_id; integer :amount}
|
@@ -147,48 +149,137 @@ describe "PostgreSQL Triggers" do
|
|
147
149
|
DB.drop_table(:entries, :accounts)
|
148
150
|
end
|
149
151
|
|
150
|
-
|
151
|
-
DB[:accounts].order(:id).select_map(:balance).
|
152
|
+
it "Should modify sum cache when adding, updating, or removing records" do
|
153
|
+
DB[:accounts].order(:id).select_map(:balance).must_equal [0, 0]
|
152
154
|
|
153
155
|
DB[:entries] << {:id=>1, :account_id=>1, :amount=>100}
|
154
|
-
DB[:accounts].order(:id).select_map(:balance).
|
156
|
+
DB[:accounts].order(:id).select_map(:balance).must_equal [100, 0]
|
155
157
|
|
156
158
|
DB[:entries] << {:id=>2, :account_id=>1, :amount=>200}
|
157
|
-
DB[:accounts].order(:id).select_map(:balance).
|
159
|
+
DB[:accounts].order(:id).select_map(:balance).must_equal [300, 0]
|
158
160
|
|
159
161
|
DB[:entries] << {:id=>3, :account_id=>nil, :amount=>500}
|
160
|
-
DB[:accounts].order(:id).select_map(:balance).
|
162
|
+
DB[:accounts].order(:id).select_map(:balance).must_equal [300, 0]
|
161
163
|
|
162
164
|
DB[:entries].where(:id=>3).update(:account_id=>2)
|
163
|
-
DB[:accounts].order(:id).select_map(:balance).
|
165
|
+
DB[:accounts].order(:id).select_map(:balance).must_equal [300, 500]
|
164
166
|
|
165
167
|
DB[:entries].exclude(:id=>2).update(:amount=>Sequel.*(:amount, 2))
|
166
|
-
DB[:accounts].order(:id).select_map(:balance).
|
168
|
+
DB[:accounts].order(:id).select_map(:balance).must_equal [400, 1000]
|
167
169
|
|
168
170
|
DB[:entries].where(:id=>2).update(:account_id=>2)
|
169
|
-
DB[:accounts].order(:id).select_map(:balance).
|
171
|
+
DB[:accounts].order(:id).select_map(:balance).must_equal [200, 1200]
|
170
172
|
|
171
173
|
DB[:entries].where(:id=>2).update(:account_id=>nil)
|
172
|
-
DB[:accounts].order(:id).select_map(:balance).
|
174
|
+
DB[:accounts].order(:id).select_map(:balance).must_equal [200, 1000]
|
173
175
|
|
174
176
|
DB[:entries].where(:id=>2).update(:id=>4)
|
175
|
-
DB[:accounts].order(:id).select_map(:balance).
|
177
|
+
DB[:accounts].order(:id).select_map(:balance).must_equal [200, 1000]
|
176
178
|
|
177
179
|
DB[:entries].where(:id=>4).update(:account_id=>2)
|
178
|
-
DB[:accounts].order(:id).select_map(:balance).
|
180
|
+
DB[:accounts].order(:id).select_map(:balance).must_equal [200, 1200]
|
179
181
|
|
180
182
|
DB[:entries].where(:id=>4).update(:account_id=>nil)
|
181
|
-
DB[:accounts].order(:id).select_map(:balance).
|
183
|
+
DB[:accounts].order(:id).select_map(:balance).must_equal [200, 1000]
|
182
184
|
|
183
185
|
DB[:entries].filter(:id=>4).delete
|
184
|
-
DB[:accounts].order(:id).select_map(:balance).
|
186
|
+
DB[:accounts].order(:id).select_map(:balance).must_equal [200, 1000]
|
185
187
|
|
186
188
|
DB[:entries].delete
|
187
|
-
DB[:accounts].order(:id).select_map(:balance).
|
189
|
+
DB[:accounts].order(:id).select_map(:balance).must_equal [0, 0]
|
188
190
|
end
|
189
191
|
end
|
190
192
|
|
191
|
-
|
193
|
+
describe "PostgreSQL Sum Through Many Cache Trigger" do
|
194
|
+
before do
|
195
|
+
DB.create_table(:parents){primary_key :id; integer :balance, :default=>0, :null=>false}
|
196
|
+
DB.create_table(:children){primary_key :id; integer :amount, :null=>false}
|
197
|
+
DB.create_table(:links){integer :parent_id, :null=>false; integer :child_id, :null=>false; unique [:parent_id, :child_id]}
|
198
|
+
DB.pgt_sum_through_many_cache(
|
199
|
+
:main_table=>:parents,
|
200
|
+
:sum_column=>:balance,
|
201
|
+
:summed_table=>:children,
|
202
|
+
:summed_column=>:amount,
|
203
|
+
:join_table=>:links,
|
204
|
+
:main_table_fk_column=>:parent_id,
|
205
|
+
:summed_table_fk_column=>:child_id
|
206
|
+
)
|
207
|
+
DB[:parents] << {:id=>1}
|
208
|
+
DB[:parents] << {:id=>2}
|
209
|
+
end
|
210
|
+
|
211
|
+
after do
|
212
|
+
DB.drop_table(:links, :parents, :children)
|
213
|
+
end
|
214
|
+
|
215
|
+
it "Should modify sum cache when adding, updating, or removing records" do
|
216
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [0, 0]
|
217
|
+
|
218
|
+
DB[:children] << {:id=>1, :amount=>100}
|
219
|
+
DB[:links] << {:parent_id=>1, :child_id=>1}
|
220
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [100, 0]
|
221
|
+
|
222
|
+
DB[:children] << {:id=>2, :amount=>200}
|
223
|
+
DB[:links] << {:parent_id=>1, :child_id=>2}
|
224
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [300, 0]
|
225
|
+
|
226
|
+
DB[:children] << {:id=>3, :amount=>500}
|
227
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [300, 0]
|
228
|
+
DB[:links] << {:parent_id=>2, :child_id=>3}
|
229
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [300, 500]
|
230
|
+
|
231
|
+
DB[:links].where(:parent_id=>2, :child_id=>3).update(:parent_id=>1)
|
232
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [800, 0]
|
233
|
+
|
234
|
+
DB[:children] << {:id=>4, :amount=>400}
|
235
|
+
DB[:links].where(:parent_id=>1, :child_id=>3).update(:child_id=>4)
|
236
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [700, 0]
|
237
|
+
|
238
|
+
DB[:links].where(:parent_id=>1, :child_id=>4).update(:parent_id=>2, :child_id=>3)
|
239
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [300, 500]
|
240
|
+
|
241
|
+
DB[:children].exclude(:id=>2).update(:amount=>Sequel.*(:amount, 2))
|
242
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [400, 1000]
|
243
|
+
|
244
|
+
DB[:links].where(:parent_id=>1, :child_id=>2).update(:parent_id=>2)
|
245
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [200, 1200]
|
246
|
+
|
247
|
+
DB[:links].where(:parent_id=>2, :child_id=>2).update(:parent_id=>1)
|
248
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [400, 1000]
|
249
|
+
|
250
|
+
DB[:links].where(:parent_id=>1, :child_id=>2).update(:child_id=>3)
|
251
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [1200, 1000]
|
252
|
+
|
253
|
+
DB[:links] << {:parent_id=>2, :child_id=>4}
|
254
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [1200, 1800]
|
255
|
+
|
256
|
+
DB[:children].filter(:id=>4).delete
|
257
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [1200, 1000]
|
258
|
+
|
259
|
+
DB[:links].filter(:parent_id=>1, :child_id=>1).delete
|
260
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [1000, 1000]
|
261
|
+
|
262
|
+
DB[:children] << {:id=>4, :amount=>400}
|
263
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [1000, 1400]
|
264
|
+
|
265
|
+
DB[:children].delete
|
266
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [0, 0]
|
267
|
+
|
268
|
+
DB[:children].multi_insert([{:id=>2, :amount=>200}, {:id=>1, :amount=>200}, {:id=>3, :amount=>1000}, {:id=>4, :amount=>400}])
|
269
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [1000, 1400]
|
270
|
+
|
271
|
+
DB[:links].where(:child_id=>3).update(:child_id=>2)
|
272
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [200, 600]
|
273
|
+
|
274
|
+
DB[:children].update(:amount=>10)
|
275
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [10, 20]
|
276
|
+
|
277
|
+
DB[:links].delete
|
278
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [0, 0]
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
describe "PostgreSQL Updated At Trigger" do
|
192
283
|
before do
|
193
284
|
DB.create_table(:accounts){integer :id; timestamp :changed_on}
|
194
285
|
DB.pgt_updated_at(:accounts, :changed_on)
|
@@ -198,19 +289,19 @@ describe "PostgreSQL Triggers" do
|
|
198
289
|
DB.drop_table(:accounts)
|
199
290
|
end
|
200
291
|
|
201
|
-
|
292
|
+
it "Should set the column always to the current timestamp" do
|
202
293
|
DB[:accounts] << {:id=>1}
|
203
294
|
t = DB[:accounts].get(:changed_on)
|
204
|
-
t.strftime('%F').
|
295
|
+
t.strftime('%F').must_equal Date.today.strftime('%F')
|
205
296
|
DB[:accounts] << {:id=>2}
|
206
297
|
ds = DB[:accounts].select(:changed_on)
|
207
|
-
DB[:accounts].select((Sequel::SQL::NumericExpression.new(:NOOP, ds.filter(:id=>2)) > ds.filter(:id=>1)).as(:x)).first[:x].
|
298
|
+
DB[:accounts].select((Sequel::SQL::NumericExpression.new(:NOOP, ds.filter(:id=>2)) > ds.filter(:id=>1)).as(:x)).first[:x].must_equal true
|
208
299
|
DB[:accounts].filter(:id=>1).update(:id=>3)
|
209
|
-
DB[:accounts].select((Sequel::SQL::NumericExpression.new(:NOOP, ds.filter(:id=>3)) > ds.filter(:id=>2)).as(:x)).first[:x].
|
300
|
+
DB[:accounts].select((Sequel::SQL::NumericExpression.new(:NOOP, ds.filter(:id=>3)) > ds.filter(:id=>2)).as(:x)).first[:x].must_equal true
|
210
301
|
end
|
211
302
|
end
|
212
303
|
|
213
|
-
|
304
|
+
describe "PostgreSQL Touch Trigger" do
|
214
305
|
before do
|
215
306
|
DB.create_table(:parents){integer :id1; integer :id2; integer :child_id; timestamp :changed_on}
|
216
307
|
DB.create_table(:children){integer :id; integer :parent_id1; integer :parent_id2; timestamp :changed_on}
|
@@ -220,86 +311,86 @@ describe "PostgreSQL Triggers" do
|
|
220
311
|
DB.drop_table(:children, :parents)
|
221
312
|
end
|
222
313
|
|
223
|
-
|
314
|
+
it "Should update the timestamp column of the related table when adding, updating or removing records" do
|
224
315
|
DB.pgt_touch(:children, :parents, :changed_on, :id1=>:parent_id1)
|
225
316
|
d = Date.today
|
226
317
|
d30 = Date.today - 30
|
227
318
|
DB[:parents] << {:id1=>1, :changed_on=>d30}
|
228
319
|
DB[:parents] << {:id1=>2, :changed_on=>d30}
|
229
320
|
DB[:children] << {:id=>1, :parent_id1=>1}
|
230
|
-
DB[:parents].order(:id1).select_map(:changed_on).map{|t| t.strftime('%F')}.
|
321
|
+
DB[:parents].order(:id1).select_map(:changed_on).map{|t| t.strftime('%F')}.must_equal [d.strftime('%F'), d30.strftime('%F')]
|
231
322
|
|
232
323
|
DB[:parents].update(:changed_on=>d30)
|
233
324
|
DB[:children].update(:id=>2)
|
234
|
-
DB[:parents].order(:id1).select_map(:changed_on).map{|t| t.strftime('%F')}.
|
325
|
+
DB[:parents].order(:id1).select_map(:changed_on).map{|t| t.strftime('%F')}.must_equal [d.strftime('%F'), d30.strftime('%F')]
|
235
326
|
|
236
327
|
DB[:parents].update(:changed_on=>d30)
|
237
328
|
DB[:children].update(:parent_id1=>2)
|
238
|
-
DB[:parents].order(:id1).select_map(:changed_on).map{|t| t.strftime('%F')}.
|
329
|
+
DB[:parents].order(:id1).select_map(:changed_on).map{|t| t.strftime('%F')}.must_equal [d.strftime('%F'), d.strftime('%F')]
|
239
330
|
|
240
331
|
DB[:parents].update(:changed_on=>d30)
|
241
332
|
DB[:children].update(:parent_id1=>nil)
|
242
|
-
DB[:parents].order(:id1).select_map(:changed_on).map{|t| t.strftime('%F')}.
|
333
|
+
DB[:parents].order(:id1).select_map(:changed_on).map{|t| t.strftime('%F')}.must_equal [d30.strftime('%F'), d.strftime('%F')]
|
243
334
|
|
244
335
|
DB[:parents].update(:changed_on=>d30)
|
245
336
|
DB[:children].update(:parent_id2=>1)
|
246
|
-
DB[:parents].order(:id1).select_map(:changed_on).map{|t| t.strftime('%F')}.
|
337
|
+
DB[:parents].order(:id1).select_map(:changed_on).map{|t| t.strftime('%F')}.must_equal [d30.strftime('%F'), d30.strftime('%F')]
|
247
338
|
|
248
339
|
DB[:parents].update(:changed_on=>d30)
|
249
340
|
DB[:children].update(:parent_id1=>2)
|
250
|
-
DB[:parents].order(:id1).select_map(:changed_on).map{|t| t.strftime('%F')}.
|
341
|
+
DB[:parents].order(:id1).select_map(:changed_on).map{|t| t.strftime('%F')}.must_equal [d30.strftime('%F'), d.strftime('%F')]
|
251
342
|
|
252
343
|
DB[:parents].update(:changed_on=>d30)
|
253
344
|
DB[:children].delete
|
254
|
-
DB[:parents].order(:id1).select_map(:changed_on).map{|t| t.strftime('%F')}.
|
345
|
+
DB[:parents].order(:id1).select_map(:changed_on).map{|t| t.strftime('%F')}.must_equal [d30.strftime('%F'), d.strftime('%F')]
|
255
346
|
|
256
347
|
DB[:parents].update(:changed_on=>d30)
|
257
348
|
DB[:children] << {:id=>2, :parent_id1=>nil}
|
258
|
-
DB[:parents].order(:id1).select_map(:changed_on).map{|t| t.strftime('%F')}.
|
349
|
+
DB[:parents].order(:id1).select_map(:changed_on).map{|t| t.strftime('%F')}.must_equal [d30.strftime('%F'), d30.strftime('%F')]
|
259
350
|
DB[:children].where(:id=>2).delete
|
260
|
-
DB[:parents].order(:id1).select_map(:changed_on).map{|t| t.strftime('%F')}.
|
351
|
+
DB[:parents].order(:id1).select_map(:changed_on).map{|t| t.strftime('%F')}.must_equal [d30.strftime('%F'), d30.strftime('%F')]
|
261
352
|
end
|
262
353
|
|
263
|
-
|
354
|
+
it "Should update the timestamp column of the related table when there is a composite foreign key" do
|
264
355
|
DB.pgt_touch(:children, :parents, :changed_on, :id1=>:parent_id1, :id2=>:parent_id2)
|
265
356
|
DB[:parents] << {:id1=>1, :id2=>2, :changed_on=>Date.today - 30}
|
266
357
|
DB[:children] << {:id=>1, :parent_id1=>1, :parent_id2=>2}
|
267
|
-
DB[:parents].get(:changed_on).strftime('%F').
|
358
|
+
DB[:parents].get(:changed_on).strftime('%F').must_equal Date.today.strftime('%F')
|
268
359
|
DB[:parents].update(:changed_on=>Date.today - 30)
|
269
360
|
DB[:children].update(:id=>2)
|
270
|
-
DB[:parents].get(:changed_on).strftime('%F').
|
361
|
+
DB[:parents].get(:changed_on).strftime('%F').must_equal Date.today.strftime('%F')
|
271
362
|
DB[:parents].update(:changed_on=>Date.today - 30)
|
272
363
|
DB[:children].delete
|
273
|
-
DB[:parents].get(:changed_on).strftime('%F').
|
364
|
+
DB[:parents].get(:changed_on).strftime('%F').must_equal Date.today.strftime('%F')
|
274
365
|
end
|
275
366
|
|
276
|
-
|
367
|
+
it "Should update timestamps correctly when two tables touch each other" do
|
277
368
|
DB.pgt_touch(:children, :parents, :changed_on, :id1=>:parent_id1)
|
278
369
|
DB.pgt_touch(:parents, :children, :changed_on, :id=>:child_id)
|
279
370
|
DB[:parents] << {:id1=>1, :child_id=>1, :changed_on=>Date.today - 30}
|
280
371
|
DB[:children] << {:id=>1, :parent_id1=>1, :changed_on=>Date.today - 30}
|
281
|
-
DB[:parents].get(:changed_on).strftime('%F').
|
282
|
-
DB[:children].get(:changed_on).strftime('%F').
|
372
|
+
DB[:parents].get(:changed_on).strftime('%F').must_equal Date.today.strftime('%F')
|
373
|
+
DB[:children].get(:changed_on).strftime('%F').must_equal Date.today.strftime('%F')
|
283
374
|
time = DB[:parents].get(:changed_on)
|
284
375
|
DB[:parents].update(:id2=>4)
|
285
|
-
DB[:parents].get(:changed_on).
|
286
|
-
DB[:children].get(:changed_on).
|
376
|
+
DB[:parents].get(:changed_on).must_be :>, time
|
377
|
+
DB[:children].get(:changed_on).must_be :>, time
|
287
378
|
time = DB[:parents].get(:changed_on)
|
288
379
|
DB[:children].update(:id=>1)
|
289
|
-
DB[:parents].get(:changed_on).
|
290
|
-
DB[:children].get(:changed_on).
|
380
|
+
DB[:parents].get(:changed_on).must_be :>, time
|
381
|
+
DB[:children].get(:changed_on).must_be :>, time
|
291
382
|
time = DB[:parents].get(:changed_on)
|
292
383
|
DB[:children].delete
|
293
|
-
DB[:parents].get(:changed_on).
|
384
|
+
DB[:parents].get(:changed_on).must_be :>, time
|
294
385
|
end
|
295
386
|
|
296
|
-
|
387
|
+
it "Should update the timestamp on the related table if that timestamp is initially NULL" do
|
297
388
|
DB.pgt_touch(:children, :parents, :changed_on, :id1=>:parent_id1)
|
298
389
|
DB[:parents] << {:id1=>1, :changed_on=>nil}
|
299
390
|
DB[:children] << {:id=>1, :parent_id1=>1}
|
300
391
|
changed_on = DB[:parents].get(:changed_on)
|
301
|
-
changed_on.
|
302
|
-
changed_on.strftime('%F').
|
392
|
+
changed_on.wont_equal nil
|
393
|
+
changed_on.strftime('%F').must_equal Date.today.strftime('%F')
|
303
394
|
end
|
304
395
|
end
|
305
396
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel_postgresql_triggers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-07-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -62,7 +62,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
62
62
|
version: '0'
|
63
63
|
requirements: []
|
64
64
|
rubyforge_project:
|
65
|
-
rubygems_version: 2.
|
65
|
+
rubygems_version: 2.5.1
|
66
66
|
signing_key:
|
67
67
|
specification_version: 4
|
68
68
|
summary: Database enforced timestamps, immutable columns, and counter/sum caches
|