sequel_postgresql_triggers 1.5.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.rdoc +13 -1
- data/lib/sequel/extensions/pg_triggers.rb +4 -4
- data/spec/sequel_postgresql_triggers_spec.rb +71 -5
- metadata +35 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a985c677d8d3b87630378feee9b80dda40ef2ba95147ec525f2df8feff18b38
|
4
|
+
data.tar.gz: 8c6b9526bbedb13a72fd3edc44c2da56041fc985c0e41187613914bae9163230
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 39bd5f9c742aeed9331e59b521d571e5631c1b3dcd0804dd959e010a2a6c2bcd110ad8752eac92b1f9d0a768d0a9925f67cf59cdc5a34b718bc4e15e07ba996e
|
7
|
+
data.tar.gz: e5304e315766b8059adc6d29e48b6eb60af4ead1662a70d0eb5d7a87daa541a3b5068385a0b3a302bddaf23fd35f88637c6c3cf762b8308d5f2b5c27f9db682e
|
data/README.rdoc
CHANGED
@@ -126,7 +126,7 @@ sum cache that sums the length of a string column.
|
|
126
126
|
|
127
127
|
Arguments:
|
128
128
|
main_table :: name of table holding counter cache column
|
129
|
-
main_table_id_column :: column in main table matching
|
129
|
+
main_table_id_column :: column in main table matching summed_table_id_column in summed_table
|
130
130
|
sum_column :: column in main table containing the sum cache
|
131
131
|
summed_table :: name of table being summed
|
132
132
|
summed_table_id_column :: column in summed_table matching main_table_id_column in main_table
|
@@ -254,6 +254,18 @@ function :: The name of the trigger function to call to log changes
|
|
254
254
|
Note that it is probably a bad idea to use the same table argument
|
255
255
|
to both +pgt_json_audit_log_setup+ and +pgt_json_audit_log+.
|
256
256
|
|
257
|
+
== Caveats
|
258
|
+
|
259
|
+
If you have defined counter or sum cache triggers using this library
|
260
|
+
before version 1.6.0, you should drop them and regenerate them if
|
261
|
+
you want the triggers to work correctly with queries that use
|
262
|
+
<tt>INSERT ... ON CONFLICT DO NOTHING</tt>.
|
263
|
+
|
264
|
+
When restoring a data-only migration with +pg_dump+, you may need to
|
265
|
+
use <tt>--disable-triggers</tt> for it to restore correctly, and you
|
266
|
+
will need to manually enforce data integrity if you are doing
|
267
|
+
partial restores and not full restores.
|
268
|
+
|
257
269
|
== License
|
258
270
|
|
259
271
|
This library is released under the MIT License. See the MIT-LICENSE
|
@@ -14,7 +14,7 @@ module Sequel
|
|
14
14
|
main_column = quote_identifier(main_table_id_column)
|
15
15
|
count_column = quote_identifier(counter_column)
|
16
16
|
|
17
|
-
pgt_trigger(counted_table, trigger_name, function_name, [:insert, :update, :delete], <<-SQL)
|
17
|
+
pgt_trigger(counted_table, trigger_name, function_name, [:insert, :update, :delete], <<-SQL, :after=>true)
|
18
18
|
BEGIN
|
19
19
|
IF (TG_OP = 'UPDATE' AND (NEW.#{id_column} = OLD.#{id_column} OR (OLD.#{id_column} IS NULL AND NEW.#{id_column} IS NULL))) THEN
|
20
20
|
RETURN NEW;
|
@@ -122,7 +122,7 @@ module Sequel
|
|
122
122
|
main_column = quote_identifier(main_table_id_column)
|
123
123
|
sum_column = quote_identifier(sum_column)
|
124
124
|
|
125
|
-
pgt_trigger(summed_table, trigger_name, function_name, [:insert, :delete, :update], <<-SQL)
|
125
|
+
pgt_trigger(summed_table, trigger_name, function_name, [:insert, :delete, :update], <<-SQL, :after=>true)
|
126
126
|
BEGIN
|
127
127
|
IF (TG_OP = 'UPDATE' AND NEW.#{id_column} = OLD.#{id_column}) THEN
|
128
128
|
UPDATE #{table} SET #{sum_column} = #{sum_column} + #{new_table_summed_column} - #{old_table_summed_column} WHERE #{main_column} = NEW.#{id_column};
|
@@ -176,7 +176,7 @@ module Sequel
|
|
176
176
|
main_table_fk_column = quote_schema_table(main_table_fk_column)
|
177
177
|
summed_table_fk_column = quote_schema_table(summed_table_fk_column)
|
178
178
|
|
179
|
-
pgt_trigger(orig_summed_table, trigger_name, function_name, [:insert, :delete, :update], <<-SQL)
|
179
|
+
pgt_trigger(orig_summed_table, trigger_name, function_name, [:insert, :delete, :update], <<-SQL, :after=>true)
|
180
180
|
BEGIN
|
181
181
|
IF (TG_OP = 'UPDATE' AND NEW.#{summed_table_id_column} = OLD.#{summed_table_id_column}) THEN
|
182
182
|
UPDATE #{main_table} SET #{sum_column} = #{sum_column} + #{new_table_summed_column} - #{old_table_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});
|
@@ -195,7 +195,7 @@ module Sequel
|
|
195
195
|
END;
|
196
196
|
SQL
|
197
197
|
|
198
|
-
pgt_trigger(orig_join_table, join_trigger_name, join_function_name, [:insert, :delete, :update], <<-SQL)
|
198
|
+
pgt_trigger(orig_join_table, join_trigger_name, join_function_name, [:insert, :delete, :update], <<-SQL, :after=>true)
|
199
199
|
BEGIN
|
200
200
|
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
|
201
201
|
IF (TG_OP = 'INSERT' OR TG_OP = 'UPDATE') THEN
|
@@ -1,10 +1,21 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
require 'rubygems'
|
3
2
|
require 'sequel'
|
4
3
|
|
4
|
+
if coverage = ENV.delete('COVERAGE')
|
5
|
+
require 'simplecov'
|
6
|
+
|
7
|
+
SimpleCov.start do
|
8
|
+
enable_coverage :branch
|
9
|
+
command_name coverage
|
10
|
+
add_filter "/spec/"
|
11
|
+
add_group('Missing'){|src| src.covered_percent < 100}
|
12
|
+
add_group('Covered'){|src| src.covered_percent == 100}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
5
16
|
ENV['MT_NO_PLUGINS'] = '1' # Work around stupid autoloading of plugins
|
6
17
|
gem 'minitest'
|
7
|
-
require 'minitest/autorun'
|
18
|
+
require 'minitest/global_expectations/autorun'
|
8
19
|
|
9
20
|
DB = Sequel.connect(ENV['PGT_SPEC_DB']||'postgres:///spgt_test?user=postgres')
|
10
21
|
|
@@ -32,6 +43,14 @@ describe "PostgreSQL Counter Cache Trigger" do
|
|
32
43
|
DB.drop_function(:spgt_counter_cache)
|
33
44
|
end
|
34
45
|
|
46
|
+
it "should not modify counter cache if adding/removing records fails due to ON CONFLICT DO NOTHING" do
|
47
|
+
DB.alter_table(:entries){add_unique_constraint :account_id}
|
48
|
+
DB[:entries].insert(:id=>1, :account_id=>1)
|
49
|
+
DB[:accounts].order(:id).select_map(:num_entries).must_equal [1, 0]
|
50
|
+
DB[:entries].insert_conflict.insert(:id=>1, :account_id=>1)
|
51
|
+
DB[:accounts].order(:id).select_map(:num_entries).must_equal [1, 0]
|
52
|
+
end
|
53
|
+
|
35
54
|
it "should modify counter cache when adding or removing records" do
|
36
55
|
DB[:accounts].order(:id).select_map(:num_entries).must_equal [0, 0]
|
37
56
|
|
@@ -129,6 +148,23 @@ describe "PostgreSQL Immutable Trigger" do
|
|
129
148
|
end
|
130
149
|
end
|
131
150
|
|
151
|
+
describe "PostgreSQL Immutable Trigger" do
|
152
|
+
before do
|
153
|
+
DB.create_table(:accounts){integer :id; integer :balance, :default=>0}
|
154
|
+
DB.pgt_immutable(:accounts, :balance)
|
155
|
+
DB[:accounts].insert(:id=>1)
|
156
|
+
end
|
157
|
+
|
158
|
+
after do
|
159
|
+
DB.drop_table(:accounts)
|
160
|
+
DB.drop_function(:pgt_im_balance)
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should work when called without options" do
|
164
|
+
DB[:accounts].update(:id=>2)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
132
168
|
describe "PostgreSQL Sum Cache Trigger" do
|
133
169
|
before do
|
134
170
|
DB.create_table(:accounts){integer :id; integer :balance, :default=>0}
|
@@ -143,6 +179,14 @@ describe "PostgreSQL Sum Cache Trigger" do
|
|
143
179
|
DB.drop_function(:spgt_sum_cache)
|
144
180
|
end
|
145
181
|
|
182
|
+
it "should not modify sum cache if adding/removing records fails due to ON CONFLICT DO NOTHING" do
|
183
|
+
DB.alter_table(:entries){add_unique_constraint :account_id}
|
184
|
+
DB[:entries].insert(:id=>1, :account_id=>1, :amount=>5)
|
185
|
+
DB[:accounts].order(:id).select_map(:balance).must_equal [5, 0]
|
186
|
+
DB[:entries].insert_conflict.insert(:id=>1, :account_id=>1, :amount=>10)
|
187
|
+
DB[:accounts].order(:id).select_map(:balance).must_equal [5, 0]
|
188
|
+
end
|
189
|
+
|
146
190
|
it "should modify sum cache when adding, updating, or removing records" do
|
147
191
|
DB[:accounts].order(:id).select_map(:balance).must_equal [0, 0]
|
148
192
|
|
@@ -266,6 +310,17 @@ describe "PostgreSQL Sum Through Many Cache Trigger" do
|
|
266
310
|
DB.drop_function(:spgt_stm_cache_join)
|
267
311
|
end
|
268
312
|
|
313
|
+
it "should not modify sum cache if adding/removing records fails due to ON CONFLICT DO NOTHING" do
|
314
|
+
DB.alter_table(:children){add_unique_constraint :amount}
|
315
|
+
DB[:children].insert(:id=>1, :amount=>5)
|
316
|
+
DB[:links].insert(:parent_id=>1, :child_id=>1)
|
317
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [5, 0]
|
318
|
+
DB[:children].insert_conflict.insert(:id=>1, :amount=>5)
|
319
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [5, 0]
|
320
|
+
DB[:links].insert_conflict.insert(:parent_id=>1, :child_id=>1)
|
321
|
+
DB[:parents].order(:id).select_map(:balance).must_equal [5, 0]
|
322
|
+
end
|
323
|
+
|
269
324
|
it "should modify sum cache when adding, updating, or removing records" do
|
270
325
|
DB[:parents].order(:id).select_map(:balance).must_equal [0, 0]
|
271
326
|
|
@@ -461,6 +516,17 @@ describe "PostgreSQL Touch Trigger" do
|
|
461
516
|
DB.drop_function(:spgt_touch2) if @spgt_touch2
|
462
517
|
end
|
463
518
|
|
519
|
+
it "should not modify timestamp column of related table if adding/removing records fails due to ON CONFLICT DO NOTHING" do
|
520
|
+
DB.pgt_touch(:children, :parents, :changed_on, {:id1=>:parent_id1}, :function_name=>:spgt_touch)
|
521
|
+
DB.alter_table(:children){add_unique_constraint :parent_id1}
|
522
|
+
d30 = Date.today - 30
|
523
|
+
DB[:children].insert(:id=>1, :parent_id1=>1)
|
524
|
+
DB[:parents].insert(:id1=>1, :changed_on=>d30)
|
525
|
+
DB[:parents].get(:changed_on).to_date.must_equal d30
|
526
|
+
DB[:children].insert_conflict.insert(:id=>1, :parent_id1=>1)
|
527
|
+
DB[:parents].get(:changed_on).to_date.must_equal d30
|
528
|
+
end
|
529
|
+
|
464
530
|
it "should update the timestamp column of the related table when adding, updating or removing records" do
|
465
531
|
DB.pgt_touch(:children, :parents, :changed_on, {:id1=>:parent_id1}, :function_name=>:spgt_touch)
|
466
532
|
d = Date.today
|
@@ -624,13 +690,13 @@ describe "PostgreSQL JSON Audit Logging" do
|
|
624
690
|
DB.drop_function(:spgt_audit_log)
|
625
691
|
end
|
626
692
|
|
627
|
-
it "should previous values in JSON format for
|
693
|
+
it "should store previous values in JSON format in audit table for updates and deletes on main table" do
|
628
694
|
@logs.first.must_be_nil
|
629
695
|
|
630
696
|
@ds.update(:id=>2, :a=>3)
|
631
697
|
@ds.all.must_equal [{:id=>2, :a=>3}]
|
632
698
|
h = @logs.first
|
633
|
-
h.delete(:at).to_i.
|
699
|
+
h.delete(:at).to_i.must_be_close_to(10, DB.get(Sequel::CURRENT_TIMESTAMP).to_i)
|
634
700
|
h.delete(:user).must_be_kind_of(String)
|
635
701
|
txid1 = h.delete(:txid)
|
636
702
|
txid1.must_be_kind_of(Integer)
|
@@ -639,7 +705,7 @@ describe "PostgreSQL JSON Audit Logging" do
|
|
639
705
|
@ds.delete
|
640
706
|
@ds.all.must_equal []
|
641
707
|
h = @logs.first
|
642
|
-
h.delete(:at).to_i.
|
708
|
+
h.delete(:at).to_i.must_be_close_to(10, DB.get(Sequel::CURRENT_TIMESTAMP).to_i)
|
643
709
|
h.delete(:user).must_be_kind_of(String)
|
644
710
|
txid2 = h.delete(:txid)
|
645
711
|
txid2.must_be_kind_of(Integer)
|
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.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -24,7 +24,35 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
-
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '5'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest-global_expectations
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description:
|
28
56
|
email: code@jeremyevans.net
|
29
57
|
executables: []
|
30
58
|
extensions: []
|
@@ -39,7 +67,7 @@ homepage: https://github.com/jeremyevans/sequel_postgresql_triggers
|
|
39
67
|
licenses:
|
40
68
|
- MIT
|
41
69
|
metadata: {}
|
42
|
-
post_install_message:
|
70
|
+
post_install_message:
|
43
71
|
rdoc_options:
|
44
72
|
- "--inline-source"
|
45
73
|
- "--line-numbers"
|
@@ -62,9 +90,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
62
90
|
- !ruby/object:Gem::Version
|
63
91
|
version: '0'
|
64
92
|
requirements: []
|
65
|
-
|
66
|
-
|
67
|
-
signing_key:
|
93
|
+
rubygems_version: 3.5.3
|
94
|
+
signing_key:
|
68
95
|
specification_version: 4
|
69
96
|
summary: Database enforced timestamps, immutable columns, counter/sum caches, and
|
70
97
|
touch propogation
|