sequel_postgresql_triggers 1.3.0 → 1.4.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 +5 -5
- data/MIT-LICENSE +1 -1
- data/README.rdoc +21 -0
- data/lib/sequel/extensions/pg_triggers.rb +60 -0
- data/spec/sequel_postgresql_triggers_spec.rb +94 -53
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 3b5d81f0071528eb70130d8ebe3093a365c0d024e250583d8ea7e35d5b7af263
|
4
|
+
data.tar.gz: 18bb2a79d2642636f34fce1f0f4be315e90592c3be8c8a8d5f587d753061d360
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 615949150a244df98c696da8472ebeb40ae5c09812457f5ecca61197a10754e489c431a68d2a44df91eb5f17e229e6e39b4d3b4f4cf6ca28be520b2145345e3c
|
7
|
+
data.tar.gz: ca67361ebd569731121ad3f482e47d0320d0658144fd4d7bd21734fcf3542ae84d3fb592483813980fbb4f7e8b52866575c4f0c7efb8566d34760d884f66207c
|
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -7,6 +7,7 @@ a user to easily handle the following types of columns:
|
|
7
7
|
* Counter/Sum Caches
|
8
8
|
* Immutable Columns
|
9
9
|
* Touch Propogation
|
10
|
+
* Foreign Key Arrays (Referential Integrity Checks)
|
10
11
|
|
11
12
|
It handles these internally to the database via triggers, so even if
|
12
13
|
other applications access the database (without using Sequel), things
|
@@ -177,6 +178,26 @@ column :: name of timestamp column to be touched
|
|
177
178
|
expr :: hash or array that represents the columns that define the relationship
|
178
179
|
opts :: options hash
|
179
180
|
|
181
|
+
=== Foreign Key Arrays - pgt_foreign_key_array
|
182
|
+
|
183
|
+
This takes a single options hash, and sets up triggers on both
|
184
|
+
tables involved. The table with the foreign key array has insert/update
|
185
|
+
triggers to make sure newly inserted/updated rows reference valid rows
|
186
|
+
in the referenced table. The table being referenced has update/delete
|
187
|
+
triggers to make sure the value before update or delete is not still
|
188
|
+
being referenced.
|
189
|
+
|
190
|
+
Note that this will not catch all referential integrity violations, but
|
191
|
+
it should catch the most common ones.
|
192
|
+
|
193
|
+
Options:
|
194
|
+
:table :: table with foreign key array
|
195
|
+
:column :: foreign key array column
|
196
|
+
:referenced_table :: table referenced by foreign key array
|
197
|
+
:referenced_column :: column referenced by foreign key array (generally primary key)
|
198
|
+
:referenced_function_name :: function name for trigger function on referenced table
|
199
|
+
:referenced_trigger_name :: trigger name for referenced table
|
200
|
+
|
180
201
|
== License
|
181
202
|
|
182
203
|
This library is released under the MIT License. See the MIT-LICENSE
|
@@ -213,6 +213,66 @@ module Sequel
|
|
213
213
|
SQL
|
214
214
|
end
|
215
215
|
|
216
|
+
def pgt_foreign_key_array(opts={})
|
217
|
+
table, column, rtable, rcolumn = opts.values_at(:table, :column, :referenced_table, :referenced_column)
|
218
|
+
trigger_name = opts[:trigger_name] || "pgt_fka_#{column}"
|
219
|
+
function_name = opts[:function_name] || "pgt_fka_#{table}__#{column}"
|
220
|
+
rtrigger_name = opts[:referenced_trigger_name] || "pgt_rfka_#{column}"
|
221
|
+
rfunction_name = opts[:referenced_function_name] || "pgt_rfka_#{table}__#{column}"
|
222
|
+
col = quote_identifier(column)
|
223
|
+
tab = quote_identifier(table)
|
224
|
+
rcol = quote_identifier(rcolumn)
|
225
|
+
rtab = quote_identifier(rtable)
|
226
|
+
|
227
|
+
pgt_trigger(table, trigger_name, function_name, [:insert, :update], <<-SQL)
|
228
|
+
DECLARE
|
229
|
+
arr #{tab}.#{col}%TYPE;
|
230
|
+
temp_count1 int;
|
231
|
+
temp_count2 int;
|
232
|
+
BEGIN
|
233
|
+
arr := NEW.#{col};
|
234
|
+
temp_count1 := array_ndims(arr);
|
235
|
+
IF arr IS NULL OR temp_count1 IS NULL THEN
|
236
|
+
RETURN NEW;
|
237
|
+
END IF;
|
238
|
+
|
239
|
+
IF temp_count1 IS DISTINCT FROM 1 THEN
|
240
|
+
RAISE EXCEPTION 'Foreign key array #{tab}.#{col} has more than 1 dimension: %, dimensions: %', arr, temp_count1;
|
241
|
+
END IF;
|
242
|
+
|
243
|
+
SELECT count(*) INTO temp_count1 FROM unnest(arr);
|
244
|
+
SELECT count(*) INTO temp_count2 FROM (SELECT DISTINCT * FROM unnest(arr)) AS t;
|
245
|
+
IF temp_count1 IS DISTINCT FROM temp_count2 THEN
|
246
|
+
RAISE EXCEPTION 'Duplicate entry in foreign key array #{tab}.#{col}: %', arr;
|
247
|
+
END IF;
|
248
|
+
|
249
|
+
SELECT COUNT(*) INTO temp_count1 FROM #{rtab} WHERE #{rcol} = ANY(arr);
|
250
|
+
temp_count2 := array_length(arr, 1);
|
251
|
+
IF temp_count1 IS DISTINCT FROM temp_count2 THEN
|
252
|
+
RAISE EXCEPTION 'Entry in foreign key array #{tab}.#{col} not in referenced column #{rtab}.#{rcol}: %', arr;
|
253
|
+
END IF;
|
254
|
+
|
255
|
+
RETURN NEW;
|
256
|
+
END;
|
257
|
+
SQL
|
258
|
+
|
259
|
+
pgt_trigger(rtable, rtrigger_name, rfunction_name, [:delete, :update], <<-SQL)
|
260
|
+
DECLARE
|
261
|
+
val #{rtab}.#{rcol}%TYPE;
|
262
|
+
temp_count int;
|
263
|
+
BEGIN
|
264
|
+
val := OLD.#{rcol};
|
265
|
+
IF (TG_OP = 'DELETE') OR val IS DISTINCT FROM NEW.#{rcol} THEN
|
266
|
+
SELECT COUNT(*) INTO temp_count FROM #{tab} WHERE #{col} @> ARRAY[val];
|
267
|
+
IF temp_count IS DISTINCT FROM 0 THEN
|
268
|
+
RAISE EXCEPTION 'Entry in referenced column #{rtab}.#{rcol} still in foreign key array #{tab}.#{col}: %, count: %', val, temp_count;
|
269
|
+
END IF;
|
270
|
+
END IF;
|
271
|
+
RETURN NEW;
|
272
|
+
END;
|
273
|
+
SQL
|
274
|
+
end
|
275
|
+
|
216
276
|
private
|
217
277
|
|
218
278
|
# Add or replace a function that returns trigger to handle the action,
|
@@ -14,6 +14,7 @@ else
|
|
14
14
|
puts "Running specs with extension"
|
15
15
|
DB.extension :pg_triggers
|
16
16
|
end
|
17
|
+
DB.extension :pg_array
|
17
18
|
|
18
19
|
describe "PostgreSQL Triggers" do
|
19
20
|
before do
|
@@ -28,8 +29,8 @@ describe "PostgreSQL Triggers" do
|
|
28
29
|
DB.create_table(:accounts){integer :id; integer :num_entries, :default=>0}
|
29
30
|
DB.create_table(:entries){integer :id; integer :account_id}
|
30
31
|
DB.pgt_counter_cache(:accounts, :id, :num_entries, :entries, :account_id, :function_name=>:spgt_counter_cache)
|
31
|
-
DB[:accounts]
|
32
|
-
DB[:accounts]
|
32
|
+
DB[:accounts].insert(:id=>1)
|
33
|
+
DB[:accounts].insert(:id=>2)
|
33
34
|
end
|
34
35
|
|
35
36
|
after do
|
@@ -40,13 +41,13 @@ describe "PostgreSQL Triggers" do
|
|
40
41
|
it "Should modify counter cache when adding or removing records" do
|
41
42
|
DB[:accounts].order(:id).select_map(:num_entries).must_equal [0, 0]
|
42
43
|
|
43
|
-
DB[:entries]
|
44
|
+
DB[:entries].insert(:id=>1, :account_id=>1)
|
44
45
|
DB[:accounts].order(:id).select_map(:num_entries).must_equal [1, 0]
|
45
46
|
|
46
|
-
DB[:entries]
|
47
|
+
DB[:entries].insert(:id=>2, :account_id=>1)
|
47
48
|
DB[:accounts].order(:id).select_map(:num_entries).must_equal [2, 0]
|
48
49
|
|
49
|
-
DB[:entries]
|
50
|
+
DB[:entries].insert(:id=>3, :account_id=>nil)
|
50
51
|
DB[:accounts].order(:id).select_map(:num_entries).must_equal [2, 0]
|
51
52
|
|
52
53
|
DB[:entries].where(:id=>3).update(:account_id=>2)
|
@@ -87,12 +88,12 @@ describe "PostgreSQL Triggers" do
|
|
87
88
|
end
|
88
89
|
|
89
90
|
it "Should set the column upon insertion and ignore modifications afterward" do
|
90
|
-
DB[:accounts]
|
91
|
+
DB[:accounts].insert(:id=>1)
|
91
92
|
t = DB[:accounts].get(:added_on)
|
92
93
|
t.strftime('%F').must_equal Date.today.strftime('%F')
|
93
94
|
DB[:accounts].update(:added_on=>Date.today - 60)
|
94
95
|
DB[:accounts].get(:added_on).must_equal t
|
95
|
-
DB[:accounts]
|
96
|
+
DB[:accounts].insert(:id=>2)
|
96
97
|
ds = DB[:accounts].select(:added_on)
|
97
98
|
DB[:accounts].select((Sequel::SQL::NumericExpression.new(:NOOP, ds.filter(:id=>2)) > ds.filter(:id=>1)).as(:x)).first[:x].must_equal true
|
98
99
|
DB[:accounts].filter(:id=>1).update(:id=>3)
|
@@ -104,7 +105,7 @@ describe "PostgreSQL Triggers" do
|
|
104
105
|
before do
|
105
106
|
DB.create_table(:accounts){integer :id; integer :balance, :default=>0}
|
106
107
|
DB.pgt_immutable(:accounts, :balance, :function_name=>:spgt_immutable)
|
107
|
-
DB[:accounts]
|
108
|
+
DB[:accounts].insert(:id=>1)
|
108
109
|
end
|
109
110
|
|
110
111
|
after do
|
@@ -128,7 +129,7 @@ describe "PostgreSQL Triggers" do
|
|
128
129
|
it "Should handle NULL values correctly" do
|
129
130
|
proc{DB[:accounts].update(:balance=>nil)}.must_raise(Sequel::DatabaseError)
|
130
131
|
DB[:accounts].delete
|
131
|
-
DB[:accounts]
|
132
|
+
DB[:accounts].insert(:id=>1, :balance=>nil)
|
132
133
|
DB[:accounts].update(:balance=>nil)
|
133
134
|
proc{DB[:accounts].update(:balance=>0)}.must_raise(Sequel::DatabaseError)
|
134
135
|
end
|
@@ -139,8 +140,8 @@ describe "PostgreSQL Triggers" do
|
|
139
140
|
DB.create_table(:accounts){integer :id; integer :balance, :default=>0}
|
140
141
|
DB.create_table(:entries){integer :id; integer :account_id; integer :amount}
|
141
142
|
DB.pgt_sum_cache(:accounts, :id, :balance, :entries, :account_id, :amount, :function_name=>:spgt_sum_cache)
|
142
|
-
DB[:accounts]
|
143
|
-
DB[:accounts]
|
143
|
+
DB[:accounts].insert(:id=>1)
|
144
|
+
DB[:accounts].insert(:id=>2)
|
144
145
|
end
|
145
146
|
|
146
147
|
after do
|
@@ -151,13 +152,13 @@ describe "PostgreSQL Triggers" do
|
|
151
152
|
it "Should modify sum cache when adding, updating, or removing records" do
|
152
153
|
DB[:accounts].order(:id).select_map(:balance).must_equal [0, 0]
|
153
154
|
|
154
|
-
DB[:entries]
|
155
|
+
DB[:entries].insert(:id=>1, :account_id=>1, :amount=>100)
|
155
156
|
DB[:accounts].order(:id).select_map(:balance).must_equal [100, 0]
|
156
157
|
|
157
|
-
DB[:entries]
|
158
|
+
DB[:entries].insert(:id=>2, :account_id=>1, :amount=>200)
|
158
159
|
DB[:accounts].order(:id).select_map(:balance).must_equal [300, 0]
|
159
160
|
|
160
|
-
DB[:entries]
|
161
|
+
DB[:entries].insert(:id=>3, :account_id=>nil, :amount=>500)
|
161
162
|
DB[:accounts].order(:id).select_map(:balance).must_equal [300, 0]
|
162
163
|
|
163
164
|
DB[:entries].where(:id=>3).update(:account_id=>2)
|
@@ -194,8 +195,8 @@ describe "PostgreSQL Triggers" do
|
|
194
195
|
DB.create_table(:accounts){integer :id; integer :nonzero_entries_count, :default=>0}
|
195
196
|
DB.create_table(:entries){integer :id; integer :account_id; integer :amount}
|
196
197
|
DB.pgt_sum_cache(:accounts, :id, :nonzero_entries_count, :entries, :account_id, Sequel.case({0=>0}, 1, :amount), :function_name=>:spgt_sum_cache)
|
197
|
-
DB[:accounts]
|
198
|
-
DB[:accounts]
|
198
|
+
DB[:accounts].insert(:id=>1)
|
199
|
+
DB[:accounts].insert(:id=>2)
|
199
200
|
end
|
200
201
|
|
201
202
|
after do
|
@@ -206,13 +207,13 @@ describe "PostgreSQL Triggers" do
|
|
206
207
|
it "Should modify sum cache when adding, updating, or removing records" do
|
207
208
|
DB[:accounts].order(:id).select_map(:nonzero_entries_count).must_equal [0, 0]
|
208
209
|
|
209
|
-
DB[:entries]
|
210
|
+
DB[:entries].insert(:id=>1, :account_id=>1, :amount=>100)
|
210
211
|
DB[:accounts].order(:id).select_map(:nonzero_entries_count).must_equal [1, 0]
|
211
212
|
|
212
|
-
DB[:entries]
|
213
|
+
DB[:entries].insert(:id=>2, :account_id=>1, :amount=>200)
|
213
214
|
DB[:accounts].order(:id).select_map(:nonzero_entries_count).must_equal [2, 0]
|
214
215
|
|
215
|
-
DB[:entries]
|
216
|
+
DB[:entries].insert(:id=>3, :account_id=>nil, :amount=>500)
|
216
217
|
DB[:accounts].order(:id).select_map(:nonzero_entries_count).must_equal [2, 0]
|
217
218
|
|
218
219
|
DB[:entries].where(:id=>3).update(:account_id=>2)
|
@@ -261,8 +262,8 @@ describe "PostgreSQL Triggers" do
|
|
261
262
|
:function_name=>:spgt_stm_cache,
|
262
263
|
:join_function_name=>:spgt_stm_cache_join
|
263
264
|
)
|
264
|
-
DB[:parents]
|
265
|
-
DB[:parents]
|
265
|
+
DB[:parents].insert(:id=>1)
|
266
|
+
DB[:parents].insert(:id=>2)
|
266
267
|
end
|
267
268
|
|
268
269
|
after do
|
@@ -274,23 +275,23 @@ describe "PostgreSQL Triggers" do
|
|
274
275
|
it "Should modify sum cache when adding, updating, or removing records" do
|
275
276
|
DB[:parents].order(:id).select_map(:balance).must_equal [0, 0]
|
276
277
|
|
277
|
-
DB[:children]
|
278
|
-
DB[:links]
|
278
|
+
DB[:children].insert(:id=>1, :amount=>100)
|
279
|
+
DB[:links].insert(:parent_id=>1, :child_id=>1)
|
279
280
|
DB[:parents].order(:id).select_map(:balance).must_equal [100, 0]
|
280
281
|
|
281
|
-
DB[:children]
|
282
|
-
DB[:links]
|
282
|
+
DB[:children].insert(:id=>2, :amount=>200)
|
283
|
+
DB[:links].insert(:parent_id=>1, :child_id=>2)
|
283
284
|
DB[:parents].order(:id).select_map(:balance).must_equal [300, 0]
|
284
285
|
|
285
|
-
DB[:children]
|
286
|
+
DB[:children].insert(:id=>3, :amount=>500)
|
286
287
|
DB[:parents].order(:id).select_map(:balance).must_equal [300, 0]
|
287
|
-
DB[:links]
|
288
|
+
DB[:links].insert(:parent_id=>2, :child_id=>3)
|
288
289
|
DB[:parents].order(:id).select_map(:balance).must_equal [300, 500]
|
289
290
|
|
290
291
|
DB[:links].where(:parent_id=>2, :child_id=>3).update(:parent_id=>1)
|
291
292
|
DB[:parents].order(:id).select_map(:balance).must_equal [800, 0]
|
292
293
|
|
293
|
-
DB[:children]
|
294
|
+
DB[:children].insert(:id=>4, :amount=>400)
|
294
295
|
DB[:links].where(:parent_id=>1, :child_id=>3).update(:child_id=>4)
|
295
296
|
DB[:parents].order(:id).select_map(:balance).must_equal [700, 0]
|
296
297
|
|
@@ -309,7 +310,7 @@ describe "PostgreSQL Triggers" do
|
|
309
310
|
DB[:links].where(:parent_id=>1, :child_id=>2).update(:child_id=>3)
|
310
311
|
DB[:parents].order(:id).select_map(:balance).must_equal [1200, 1000]
|
311
312
|
|
312
|
-
DB[:links]
|
313
|
+
DB[:links].insert(:parent_id=>2, :child_id=>4)
|
313
314
|
DB[:parents].order(:id).select_map(:balance).must_equal [1200, 1800]
|
314
315
|
|
315
316
|
DB[:children].filter(:id=>4).delete
|
@@ -318,7 +319,7 @@ describe "PostgreSQL Triggers" do
|
|
318
319
|
DB[:links].filter(:parent_id=>1, :child_id=>1).delete
|
319
320
|
DB[:parents].order(:id).select_map(:balance).must_equal [1000, 1000]
|
320
321
|
|
321
|
-
DB[:children]
|
322
|
+
DB[:children].insert(:id=>4, :amount=>400)
|
322
323
|
DB[:parents].order(:id).select_map(:balance).must_equal [1000, 1400]
|
323
324
|
|
324
325
|
DB[:children].delete
|
@@ -354,8 +355,8 @@ describe "PostgreSQL Triggers" do
|
|
354
355
|
:function_name=>:spgt_stm_cache,
|
355
356
|
:join_function_name=>:spgt_stm_cache_join
|
356
357
|
)
|
357
|
-
DB[:parents]
|
358
|
-
DB[:parents]
|
358
|
+
DB[:parents].insert(:id=>1)
|
359
|
+
DB[:parents].insert(:id=>2)
|
359
360
|
end
|
360
361
|
|
361
362
|
after do
|
@@ -367,23 +368,23 @@ describe "PostgreSQL Triggers" do
|
|
367
368
|
it "Should modify sum cache when adding, updating, or removing records" do
|
368
369
|
DB[:parents].order(:id).select_map(:nonzero_entries_count).must_equal [0, 0]
|
369
370
|
|
370
|
-
DB[:children]
|
371
|
-
DB[:links]
|
371
|
+
DB[:children].insert(:id=>1, :amount=>100)
|
372
|
+
DB[:links].insert(:parent_id=>1, :child_id=>1)
|
372
373
|
DB[:parents].order(:id).select_map(:nonzero_entries_count).must_equal [1, 0]
|
373
374
|
|
374
|
-
DB[:children]
|
375
|
-
DB[:links]
|
375
|
+
DB[:children].insert(:id=>2, :amount=>200)
|
376
|
+
DB[:links].insert(:parent_id=>1, :child_id=>2)
|
376
377
|
DB[:parents].order(:id).select_map(:nonzero_entries_count).must_equal [2, 0]
|
377
378
|
|
378
|
-
DB[:children]
|
379
|
+
DB[:children].insert(:id=>3, :amount=>500)
|
379
380
|
DB[:parents].order(:id).select_map(:nonzero_entries_count).must_equal [2, 0]
|
380
|
-
DB[:links]
|
381
|
+
DB[:links].insert(:parent_id=>2, :child_id=>3)
|
381
382
|
DB[:parents].order(:id).select_map(:nonzero_entries_count).must_equal [2, 1]
|
382
383
|
|
383
384
|
DB[:links].where(:parent_id=>2, :child_id=>3).update(:parent_id=>1)
|
384
385
|
DB[:parents].order(:id).select_map(:nonzero_entries_count).must_equal [3, 0]
|
385
386
|
|
386
|
-
DB[:children]
|
387
|
+
DB[:children].insert(:id=>4, :amount=>400)
|
387
388
|
DB[:links].where(:parent_id=>1, :child_id=>3).update(:child_id=>4)
|
388
389
|
DB[:parents].order(:id).select_map(:nonzero_entries_count).must_equal [3, 0]
|
389
390
|
|
@@ -402,7 +403,7 @@ describe "PostgreSQL Triggers" do
|
|
402
403
|
DB[:links].where(:parent_id=>1, :child_id=>2).update(:child_id=>3)
|
403
404
|
DB[:parents].order(:id).select_map(:nonzero_entries_count).must_equal [2, 1]
|
404
405
|
|
405
|
-
DB[:links]
|
406
|
+
DB[:links].insert(:parent_id=>2, :child_id=>4)
|
406
407
|
DB[:parents].order(:id).select_map(:nonzero_entries_count).must_equal [2, 2]
|
407
408
|
|
408
409
|
DB[:children].filter(:id=>4).delete
|
@@ -411,7 +412,7 @@ describe "PostgreSQL Triggers" do
|
|
411
412
|
DB[:links].filter(:parent_id=>1, :child_id=>1).delete
|
412
413
|
DB[:parents].order(:id).select_map(:nonzero_entries_count).must_equal [1, 1]
|
413
414
|
|
414
|
-
DB[:children]
|
415
|
+
DB[:children].insert(:id=>4, :amount=>400)
|
415
416
|
DB[:parents].order(:id).select_map(:nonzero_entries_count).must_equal [1, 2]
|
416
417
|
|
417
418
|
DB[:children].delete
|
@@ -443,10 +444,10 @@ describe "PostgreSQL Triggers" do
|
|
443
444
|
end
|
444
445
|
|
445
446
|
it "Should set the column always to the current timestamp" do
|
446
|
-
DB[:accounts]
|
447
|
+
DB[:accounts].insert(:id=>1)
|
447
448
|
t = DB[:accounts].get(:changed_on)
|
448
449
|
t.strftime('%F').must_equal Date.today.strftime('%F')
|
449
|
-
DB[:accounts]
|
450
|
+
DB[:accounts].insert(:id=>2)
|
450
451
|
ds = DB[:accounts].select(:changed_on)
|
451
452
|
DB[:accounts].select((Sequel::SQL::NumericExpression.new(:NOOP, ds.filter(:id=>2)) > ds.filter(:id=>1)).as(:x)).first[:x].must_equal true
|
452
453
|
DB[:accounts].filter(:id=>1).update(:id=>3)
|
@@ -470,9 +471,9 @@ describe "PostgreSQL Triggers" do
|
|
470
471
|
DB.pgt_touch(:children, :parents, :changed_on, {:id1=>:parent_id1}, :function_name=>:spgt_touch)
|
471
472
|
d = Date.today
|
472
473
|
d30 = Date.today - 30
|
473
|
-
DB[:parents]
|
474
|
-
DB[:parents]
|
475
|
-
DB[:children]
|
474
|
+
DB[:parents].insert(:id1=>1, :changed_on=>d30)
|
475
|
+
DB[:parents].insert(:id1=>2, :changed_on=>d30)
|
476
|
+
DB[:children].insert(:id=>1, :parent_id1=>1)
|
476
477
|
DB[:parents].order(:id1).select_map(:changed_on).map{|t| t.strftime('%F')}.must_equal [d.strftime('%F'), d30.strftime('%F')]
|
477
478
|
|
478
479
|
DB[:parents].update(:changed_on=>d30)
|
@@ -500,7 +501,7 @@ describe "PostgreSQL Triggers" do
|
|
500
501
|
DB[:parents].order(:id1).select_map(:changed_on).map{|t| t.strftime('%F')}.must_equal [d30.strftime('%F'), d.strftime('%F')]
|
501
502
|
|
502
503
|
DB[:parents].update(:changed_on=>d30)
|
503
|
-
DB[:children]
|
504
|
+
DB[:children].insert(:id=>2, :parent_id1=>nil)
|
504
505
|
DB[:parents].order(:id1).select_map(:changed_on).map{|t| t.strftime('%F')}.must_equal [d30.strftime('%F'), d30.strftime('%F')]
|
505
506
|
DB[:children].where(:id=>2).delete
|
506
507
|
DB[:parents].order(:id1).select_map(:changed_on).map{|t| t.strftime('%F')}.must_equal [d30.strftime('%F'), d30.strftime('%F')]
|
@@ -508,8 +509,8 @@ describe "PostgreSQL Triggers" do
|
|
508
509
|
|
509
510
|
it "Should update the timestamp column of the related table when there is a composite foreign key" do
|
510
511
|
DB.pgt_touch(:children, :parents, :changed_on, {:id1=>:parent_id1, :id2=>:parent_id2}, :function_name=>:spgt_touch)
|
511
|
-
DB[:parents]
|
512
|
-
DB[:children]
|
512
|
+
DB[:parents].insert(:id1=>1, :id2=>2, :changed_on=>Date.today - 30)
|
513
|
+
DB[:children].insert(:id=>1, :parent_id1=>1, :parent_id2=>2)
|
513
514
|
DB[:parents].get(:changed_on).strftime('%F').must_equal Date.today.strftime('%F')
|
514
515
|
DB[:parents].update(:changed_on=>Date.today - 30)
|
515
516
|
DB[:children].update(:id=>2)
|
@@ -523,8 +524,8 @@ describe "PostgreSQL Triggers" do
|
|
523
524
|
DB.pgt_touch(:children, :parents, :changed_on, {:id1=>:parent_id1}, :function_name=>:spgt_touch)
|
524
525
|
@spgt_touch2 = true
|
525
526
|
DB.pgt_touch(:parents, :children, :changed_on, {:id=>:child_id}, :function_name=>:spgt_touch2)
|
526
|
-
DB[:parents]
|
527
|
-
DB[:children]
|
527
|
+
DB[:parents].insert(:id1=>1, :child_id=>1, :changed_on=>Date.today - 30)
|
528
|
+
DB[:children].insert(:id=>1, :parent_id1=>1, :changed_on=>Date.today - 30)
|
528
529
|
DB[:parents].get(:changed_on).strftime('%F').must_equal Date.today.strftime('%F')
|
529
530
|
DB[:children].get(:changed_on).strftime('%F').must_equal Date.today.strftime('%F')
|
530
531
|
time = DB[:parents].get(:changed_on)
|
@@ -542,11 +543,51 @@ describe "PostgreSQL Triggers" do
|
|
542
543
|
|
543
544
|
it "Should update the timestamp on the related table if that timestamp is initially NULL" do
|
544
545
|
DB.pgt_touch(:children, :parents, :changed_on, {:id1=>:parent_id1}, :function_name=>:spgt_touch)
|
545
|
-
DB[:parents]
|
546
|
-
DB[:children]
|
546
|
+
DB[:parents].insert(:id1=>1, :changed_on=>nil)
|
547
|
+
DB[:children].insert(:id=>1, :parent_id1=>1)
|
547
548
|
changed_on = DB[:parents].get(:changed_on)
|
548
549
|
changed_on.wont_equal nil
|
549
550
|
changed_on.strftime('%F').must_equal Date.today.strftime('%F')
|
550
551
|
end
|
551
552
|
end
|
553
|
+
|
554
|
+
describe "PostgreSQL Array Foreign Key Trigger" do
|
555
|
+
before do
|
556
|
+
DB.create_table(:accounts){Integer :id, :primary_key=>true}
|
557
|
+
DB.create_table(:entries){Integer :id, :primary_key=>true; column :account_ids, 'integer[]'}
|
558
|
+
DB.pgt_foreign_key_array(:table=>:entries, :column=>:account_ids, :referenced_table=>:accounts, :referenced_column=>:id, :function_name=>:spgt_foreign_key_array, :referenced_function_name=>:spgt_referenced_foreign_key_array)
|
559
|
+
end
|
560
|
+
|
561
|
+
after do
|
562
|
+
DB.drop_table(:entries, :accounts)
|
563
|
+
DB.drop_function(:spgt_foreign_key_array)
|
564
|
+
DB.drop_function(:spgt_referenced_foreign_key_array)
|
565
|
+
end
|
566
|
+
|
567
|
+
it "should raise error for queries that violate referential integrity, and allow other queries" do
|
568
|
+
proc{DB[:entries].insert(:id=>10, :account_ids=>Sequel.pg_array([1]))}.must_raise Sequel::DatabaseError
|
569
|
+
DB[:entries].insert(:id=>10, :account_ids=>nil)
|
570
|
+
DB[:entries].update(:account_ids=>Sequel.pg_array([], :integer))
|
571
|
+
DB[:accounts].insert(:id=>1)
|
572
|
+
proc{DB[:entries].insert(:id=>10, :account_ids=>Sequel.pg_array([1, 1]))}.must_raise Sequel::DatabaseError
|
573
|
+
DB[:entries].update(:account_ids=>Sequel.pg_array([1]))
|
574
|
+
proc{DB[:entries].update(:account_ids=>Sequel.pg_array([2]))}.must_raise Sequel::DatabaseError
|
575
|
+
DB[:accounts].insert(:id=>2)
|
576
|
+
proc{DB[:entries].insert(:id=>10, :account_ids=>Sequel.pg_array([[1], [2]]))}.must_raise Sequel::DatabaseError
|
577
|
+
DB[:entries].update(:account_ids=>Sequel.pg_array([2]))
|
578
|
+
DB[:entries].all.must_equal [{:id=>10, :account_ids=>[2]}]
|
579
|
+
DB[:entries].update(:account_ids=>Sequel.pg_array([1, 2]))
|
580
|
+
DB[:entries].all.must_equal [{:id=>10, :account_ids=>[1, 2]}]
|
581
|
+
DB[:entries].update(:account_ids=>Sequel.pg_array([1]))
|
582
|
+
DB[:accounts].where(:id=>1).update(:id=>1)
|
583
|
+
DB[:accounts].where(:id=>2).update(:id=>3)
|
584
|
+
proc{DB[:accounts].where(:id=>1).update(:id=>2)}.must_raise Sequel::DatabaseError
|
585
|
+
proc{DB[:accounts].where(:id=>1).delete}.must_raise Sequel::DatabaseError
|
586
|
+
DB[:accounts].where(:id=>3).count.must_equal 1
|
587
|
+
DB[:accounts].where(:id=>3).delete
|
588
|
+
proc{DB[:accounts].delete}.must_raise Sequel::DatabaseError
|
589
|
+
DB[:entries].delete
|
590
|
+
DB[:accounts].delete
|
591
|
+
end
|
592
|
+
end
|
552
593
|
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.
|
4
|
+
version: 1.4.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: 2018-01-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -63,7 +63,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
63
63
|
version: '0'
|
64
64
|
requirements: []
|
65
65
|
rubyforge_project:
|
66
|
-
rubygems_version: 2.
|
66
|
+
rubygems_version: 2.7.3
|
67
67
|
signing_key:
|
68
68
|
specification_version: 4
|
69
69
|
summary: Database enforced timestamps, immutable columns, counter/sum caches, and
|