sequel_postgresql_triggers 1.0.8 → 1.1.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.
- 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
|