2
-- checkpoint so that if we have a crash in the tests, replay of the
3
-- just-completed CREATE DATABASE won't discard the core dump file
8
-- Create the tables used in the test queries
10
-- T_pkey1 is the primary key table for T_dta1. Entries from T_pkey1
11
-- Cannot be changed or deleted if they are referenced from T_dta1.
13
-- T_pkey2 is the primary key table for T_dta2. If the key values in
14
-- T_pkey2 are changed, the references in T_dta2 follow. If entries
15
-- are deleted, the referencing entries from T_dta2 are deleted too.
16
-- The values for field key2 in T_pkey2 are silently converted to
17
-- upper case on insert/update.
19
create table T_pkey1 (
25
create table T_pkey2 (
45
-- Function to check key existance in T_pkey1
47
create function check_pkey1_exists(int4, bpchar) returns bool as '
48
if {![info exists GD]} {
49
set GD(plan) [spi_prepare \\
50
"select 1 from T_pkey1 \\
51
where key1 = \\$1 and key2 = \\$2" \\
55
set n [spi_execp -count 1 $GD(plan) [list $1 $2]]
65
-- Trigger function on every change to T_pkey1
67
create function trig_pkey1_before() returns trigger as '
69
# Create prepared plans on the first call
71
if {![info exists GD]} {
73
# Plan to check for duplicate key in T_pkey1
75
set GD(plan_pkey1) [spi_prepare \\
76
"select check_pkey1_exists(\\$1, \\$2) as ret" \\
79
# Plan to check for references from T_dta1
81
set GD(plan_dta1) [spi_prepare \\
82
"select 1 from T_dta1 \\
83
where ref1 = \\$1 and ref2 = \\$2" \\
96
# Must check for duplicate key on INSERT
102
# Must check for duplicate key on UPDATE only if
103
# the key changes. In that case we must check for
104
# references to OLD values too.
106
if {[string compare $NEW(key1) $OLD(key1)] != 0} {
110
if {[string compare $NEW(key2) $OLD(key2)] != 0} {
117
# Must only check for references to OLD on DELETE
123
if {$check_new_dup} {
125
# Check for duplicate key
127
spi_execp -count 1 $GD(plan_pkey1) [list $NEW(key1) $NEW(key2)]
130
"duplicate key ''$NEW(key1)'', ''$NEW(key2)'' for T_pkey1"
134
if {$check_old_ref} {
136
# Check for references to OLD
138
set n [spi_execp -count 1 $GD(plan_dta1) [list $OLD(key1) $OLD(key2)]]
141
"key ''$OLD(key1)'', ''$OLD(key2)'' referenced by T_dta1"
146
# Anything is fine - let operation pass through
152
create trigger pkey1_before before insert or update or delete on T_pkey1
153
for each row execute procedure
158
-- Trigger function to check for duplicate keys in T_pkey2
159
-- and to force key2 to be upper case only without leading whitespaces
161
create function trig_pkey2_before() returns trigger as '
163
# Prepare plan on first call
165
if {![info exists GD]} {
166
set GD(plan_pkey2) [spi_prepare \\
167
"select 1 from T_pkey2 \\
168
where key1 = \\$1 and key2 = \\$2" \\
175
set NEW(key2) [string toupper [string trim $NEW(key2)]]
178
# Check for duplicate key
180
set n [spi_execp -count 1 $GD(plan_pkey2) [list $NEW(key1) $NEW(key2)]]
183
"duplicate key ''$NEW(key1)'', ''$NEW(key2)'' for T_pkey2"
187
# Return modified tuple in NEW
189
return [array get NEW]
193
create trigger pkey2_before before insert or update on T_pkey2
194
for each row execute procedure
199
-- Trigger function to force references from T_dta2 follow changes
200
-- in T_pkey2 or be deleted too. This must be done AFTER the changes
201
-- in T_pkey2 are done so the trigger for primkey check on T_dta2
202
-- fired on our updates will see the new key values in T_pkey2.
204
create function trig_pkey2_after() returns trigger as '
206
# Prepare plans on first call
208
if {![info exists GD]} {
210
# Plan to update references from T_dta2
212
set GD(plan_dta2_upd) [spi_prepare \\
213
"update T_dta2 set ref1 = \\$3, ref2 = \\$4 \\
214
where ref1 = \\$1 and ref2 = \\$2" \\
215
{int4 bpchar int4 bpchar}]
217
# Plan to delete references from T_dta2
219
set GD(plan_dta2_del) [spi_prepare \\
220
"delete from T_dta2 \\
221
where ref1 = \\$1 and ref2 = \\$2" \\
234
# On update we must let old references follow
236
set NEW(key2) [string toupper $NEW(key2)]
238
if {[string compare $NEW(key1) $OLD(key1)] != 0} {
241
if {[string compare $NEW(key2) $OLD(key2)] != 0} {
247
# On delete we must delete references too
253
if {$old_ref_follow} {
255
# Let old references follow and fire NOTICE message if
258
set n [spi_execp $GD(plan_dta2_upd) \\
259
[list $OLD(key1) $OLD(key2) $NEW(key1) $NEW(key2)]]
262
"updated $n entries in T_dta2 for new key in T_pkey2"
266
if {$old_ref_delete} {
268
# delete references and fire NOTICE message if
271
set n [spi_execp $GD(plan_dta2_del) \\
272
[list $OLD(key1) $OLD(key2)]]
275
"deleted $n entries from T_dta2"
283
create trigger pkey2_after after update or delete on T_pkey2
284
for each row execute procedure
289
-- Generic trigger function to check references in T_dta1 and T_dta2
291
create function check_primkey() returns trigger as '
293
# For every trigger/relation pair we create
294
# a saved plan and hold them in GD
296
set plankey [list "plan" $TG_name $TG_relid]
297
set planrel [list "relname" $TG_relid]
300
# Extract the pkey relation name
302
set keyidx [expr [llength $args] / 2]
303
set keyrel [string tolower [lindex $args $keyidx]]
305
if {![info exists GD($plankey)]} {
307
# We must prepare a new plan. Build up a query string
308
# for the primary key check.
310
set keylist [lrange $args [expr $keyidx + 1] end]
312
set query "select 1 from $keyrel"
316
foreach key $keylist {
317
set key [string tolower $key]
319
# Add the qual part to the query string
321
append query "$qual $key = \\$$idx"
325
# Lookup the fields type in pg_attribute
327
set n [spi_exec "select T.typname \\
328
from pg_catalog.pg_type T, pg_catalog.pg_attribute A, pg_catalog.pg_class C \\
329
where C.relname = ''[quote $keyrel]'' \\
330
and C.oid = A.attrelid \\
331
and A.attname = ''[quote $key]'' \\
332
and A.atttypid = T.oid"]
334
elog ERROR "table $keyrel doesn''t have a field named $key"
338
# Append the fields type to the argument type list
340
lappend typlist $typname
347
set GD($plankey) [spi_prepare $query $typlist]
350
# Lookup and remember the table name for later error messages
352
spi_exec "select relname from pg_catalog.pg_class \\
353
where oid = ''$TG_relid''::oid"
354
set GD($planrel) $relname
358
# Build the argument list from the NEW row
362
foreach arg [lrange $args 0 $keyidx] {
363
lappend arglist $NEW($arg)
367
# Check for the primary key
369
set n [spi_execp -count 1 $GD($plankey) $arglist]
371
elog ERROR "key for $GD($planrel) not in $keyrel"
381
create trigger dta1_before before insert or update on T_dta1
382
for each row execute procedure
383
check_primkey('ref1', 'ref2', 'T_pkey1', 'key1', 'key2');
386
create trigger dta2_before before insert or update on T_dta2
387
for each row execute procedure
388
check_primkey('ref1', 'ref2', 'T_pkey2', 'key1', 'key2');
391
create function tcl_int4add(int4,int4) returns int4 as '
392
return [expr $1 + $2]
395
-- We use split(n) as a quick-and-dirty way of parsing the input array
396
-- value, which comes in as a string like '{1,2}'. There are better ways...
398
create function tcl_int4_accum(int4[], int4) returns int4[] as '
399
set state [split $1 "{,}"]
400
set newsum [expr {[lindex $state 1] + $2}]
401
set newcnt [expr {[lindex $state 2] + 1}]
402
return "{$newsum,$newcnt}"
405
create function tcl_int4_avg(int4[]) returns int4 as '
406
set state [split $1 "{,}"]
407
if {[lindex $state 2] == 0} { return_null }
408
return [expr {[lindex $state 1] / [lindex $state 2]}]
411
create aggregate tcl_avg (
412
sfunc = tcl_int4_accum,
415
finalfunc = tcl_int4_avg,
419
create aggregate tcl_sum (
426
create function tcl_int4lt(int4,int4) returns bool as '
436
procedure = tcl_int4lt