2 * Copyright (C) 2004-2008 Georgia Public Library Service
3 * Copyright (C) 2007-2008 Equinox Software, Inc.
4 * Mike Rylander <miker@esilibrary.com>
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
18 DROP SCHEMA action CASCADE;
24 CREATE TABLE action.in_house_use (
25 id SERIAL PRIMARY KEY,
26 item BIGINT NOT NULL REFERENCES asset.copy (id) DEFERRABLE INITIALLY DEFERRED,
27 staff INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
28 org_unit INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
29 use_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
32 CREATE TABLE action.non_cataloged_circulation (
33 id SERIAL PRIMARY KEY,
34 patron INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
35 staff INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
36 circ_lib INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
37 item_type INT NOT NULL REFERENCES config.non_cataloged_type (id) DEFERRABLE INITIALLY DEFERRED,
38 circ_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
41 CREATE TABLE action.non_cat_in_house_use (
42 id SERIAL PRIMARY KEY,
43 item_type BIGINT NOT NULL REFERENCES config.non_cataloged_type(id) DEFERRABLE INITIALLY DEFERRED,
44 staff INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
45 org_unit INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
46 use_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
49 CREATE TABLE action.survey (
50 id SERIAL PRIMARY KEY,
51 owner INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
52 start_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
53 end_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW() + '10 years'::INTERVAL,
54 usr_summary BOOL NOT NULL DEFAULT FALSE,
55 opac BOOL NOT NULL DEFAULT FALSE,
56 poll BOOL NOT NULL DEFAULT FALSE,
57 required BOOL NOT NULL DEFAULT FALSE,
59 description TEXT NOT NULL
61 CREATE UNIQUE INDEX asv_once_per_owner_idx ON action.survey (owner,name);
63 CREATE TABLE action.survey_question (
64 id SERIAL PRIMARY KEY,
65 survey INT NOT NULL REFERENCES action.survey DEFERRABLE INITIALLY DEFERRED,
66 question TEXT NOT NULL
69 CREATE TABLE action.survey_answer (
70 id SERIAL PRIMARY KEY,
71 question INT NOT NULL REFERENCES action.survey_question DEFERRABLE INITIALLY DEFERRED,
75 CREATE SEQUENCE action.survey_response_group_id_seq;
77 CREATE TABLE action.survey_response (
78 id BIGSERIAL PRIMARY KEY,
79 response_group_id INT,
80 usr INT, -- REFERENCES actor.usr
81 survey INT NOT NULL REFERENCES action.survey DEFERRABLE INITIALLY DEFERRED,
82 question INT NOT NULL REFERENCES action.survey_question DEFERRABLE INITIALLY DEFERRED,
83 answer INT NOT NULL REFERENCES action.survey_answer DEFERRABLE INITIALLY DEFERRED,
84 answer_date TIMESTAMP WITH TIME ZONE,
85 effective_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
87 CREATE OR REPLACE FUNCTION action.survey_response_answer_date_fixup () RETURNS TRIGGER AS '
89 NEW.answer_date := NOW();
93 CREATE TRIGGER action_survey_response_answer_date_fixup_tgr
94 BEFORE INSERT ON action.survey_response
96 EXECUTE PROCEDURE action.survey_response_answer_date_fixup ();
99 CREATE TABLE action.circulation (
100 target_copy BIGINT NOT NULL, -- asset.copy.id
101 circ_lib INT NOT NULL, -- actor.org_unit.id
102 circ_staff INT NOT NULL, -- actor.usr.id
103 checkin_staff INT, -- actor.usr.id
104 checkin_lib INT, -- actor.org_unit.id
105 renewal_remaining INT NOT NULL, -- derived from "circ duration" rule
106 due_date TIMESTAMP WITH TIME ZONE,
107 stop_fines_time TIMESTAMP WITH TIME ZONE,
108 checkin_time TIMESTAMP WITH TIME ZONE,
109 create_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
110 duration INTERVAL, -- derived from "circ duration" rule
111 fine_interval INTERVAL NOT NULL DEFAULT '1 day'::INTERVAL, -- derived from "circ fine" rule
112 recuring_fine NUMERIC(6,2), -- derived from "circ fine" rule
113 max_fine NUMERIC(6,2), -- derived from "max fine" rule
114 phone_renewal BOOL NOT NULL DEFAULT FALSE,
115 desk_renewal BOOL NOT NULL DEFAULT FALSE,
116 opac_renewal BOOL NOT NULL DEFAULT FALSE,
117 duration_rule TEXT NOT NULL, -- name of "circ duration" rule
118 recuring_fine_rule TEXT NOT NULL, -- name of "circ fine" rule
119 max_fine_rule TEXT NOT NULL, -- name of "max fine" rule
120 stop_fines TEXT CHECK (stop_fines IN ('CHECKIN','CLAIMSRETURNED','LOST','MAXFINES','RENEW','LONGOVERDUE'))
121 ) INHERITS (money.billable_xact);
122 ALTER TABLE action.circulation ADD PRIMARY KEY (id);
123 CREATE INDEX circ_open_xacts_idx ON action.circulation (usr) WHERE xact_finish IS NULL;
124 CREATE INDEX circ_outstanding_idx ON action.circulation (usr) WHERE checkin_time IS NULL;
125 CREATE INDEX circ_checkin_time ON "action".circulation (checkin_time) WHERE checkin_time IS NOT NULL;
126 CREATE INDEX circ_circ_lib_idx ON "action".circulation (circ_lib);
127 CREATE INDEX circ_open_date_idx ON "action".circulation (xact_start) WHERE xact_finish IS NULL;
129 CREATE TRIGGER mat_summary_create_tgr AFTER INSERT ON action.circulation FOR EACH ROW EXECUTE PROCEDURE money.mat_summary_create ();
130 CREATE TRIGGER mat_summary_change_tgr AFTER UPDATE ON action.circulation FOR EACH ROW EXECUTE PROCEDURE money.mat_summary_update ();
131 CREATE TRIGGER mat_summary_remove_tgr AFTER DELETE ON action.circulation FOR EACH ROW EXECUTE PROCEDURE money.mat_summary_delete ();
134 CREATE TABLE action.aged_circulation (
136 usr_home_ou INT NOT NULL,
137 usr_profile INT NOT NULL,
139 copy_call_number INT NOT NULL,
140 copy_location INT NOT NULL,
141 copy_owning_lib INT NOT NULL,
142 copy_circ_lib INT NOT NULL,
143 copy_bib_record BIGINT NOT NULL,
144 LIKE action.circulation
147 ALTER TABLE action.aged_circulation ADD PRIMARY KEY (id);
148 ALTER TABLE action.aged_circulation DROP COLUMN usr;
149 CREATE INDEX aged_circ_circ_lib_idx ON "action".aged_circulation (circ_lib);
150 CREATE INDEX aged_circ_start_idx ON "action".aged_circulation (xact_start);
151 CREATE INDEX aged_circ_copy_circ_lib_idx ON "action".aged_circulation (copy_circ_lib);
152 CREATE INDEX aged_circ_copy_owning_lib_idx ON "action".aged_circulation (copy_owning_lib);
153 CREATE INDEX aged_circ_copy_location_idx ON "action".aged_circulation (copy_location);
155 CREATE OR REPLACE VIEW action.all_circulation AS
156 SELECT id,usr_post_code, usr_home_ou, usr_profile, usr_birth_year, copy_call_number, copy_location,
157 copy_owning_lib, copy_circ_lib, copy_bib_record, xact_start, xact_finish, target_copy,
158 circ_lib, circ_staff, checkin_staff, checkin_lib, renewal_remaining, due_date,
159 stop_fines_time, checkin_time, create_time, duration, fine_interval, recuring_fine,
160 max_fine, phone_renewal, desk_renewal, opac_renewal, duration_rule, recuring_fine_rule,
161 max_fine_rule, stop_fines
162 FROM action.aged_circulation
164 SELECT DISTINCT circ.id,COALESCE(a.post_code,b.post_code) AS usr_post_code, p.home_ou AS usr_home_ou, p.profile AS usr_profile, EXTRACT(YEAR FROM p.dob)::INT AS usr_birth_year,
165 cp.call_number AS copy_call_number, cp.location AS copy_location, cn.owning_lib AS copy_owning_lib, cp.circ_lib AS copy_circ_lib,
166 cn.record AS copy_bib_record, circ.xact_start, circ.xact_finish, circ.target_copy, circ.circ_lib, circ.circ_staff, circ.checkin_staff,
167 circ.checkin_lib, circ.renewal_remaining, circ.due_date, circ.stop_fines_time, circ.checkin_time, circ.create_time, circ.duration,
168 circ.fine_interval, circ.recuring_fine, circ.max_fine, circ.phone_renewal, circ.desk_renewal, circ.opac_renewal, circ.duration_rule,
169 circ.recuring_fine_rule, circ.max_fine_rule, circ.stop_fines
170 FROM action.circulation circ
171 JOIN asset.copy cp ON (circ.target_copy = cp.id)
172 JOIN asset.call_number cn ON (cp.call_number = cn.id)
173 JOIN actor.usr p ON (circ.usr = p.id)
174 LEFT JOIN actor.usr_address a ON (p.mailing_address = a.id)
175 LEFT JOIN actor.usr_address b ON (p.billing_address = a.id);
177 CREATE OR REPLACE FUNCTION action.age_circ_on_delete () RETURNS TRIGGER AS $$
179 INSERT INTO action.aged_circulation
180 (id,usr_post_code, usr_home_ou, usr_profile, usr_birth_year, copy_call_number, copy_location,
181 copy_owning_lib, copy_circ_lib, copy_bib_record, xact_start, xact_finish, target_copy,
182 circ_lib, circ_staff, checkin_staff, checkin_lib, renewal_remaining, due_date,
183 stop_fines_time, checkin_time, create_time, duration, fine_interval, recuring_fine,
184 max_fine, phone_renewal, desk_renewal, opac_renewal, duration_rule, recuring_fine_rule,
185 max_fine_rule, stop_fines)
187 id,usr_post_code, usr_home_ou, usr_profile, usr_birth_year, copy_call_number, copy_location,
188 copy_owning_lib, copy_circ_lib, copy_bib_record, xact_start, xact_finish, target_copy,
189 circ_lib, circ_staff, checkin_staff, checkin_lib, renewal_remaining, due_date,
190 stop_fines_time, checkin_time, create_time, duration, fine_interval, recuring_fine,
191 max_fine, phone_renewal, desk_renewal, opac_renewal, duration_rule, recuring_fine_rule,
192 max_fine_rule, stop_fines
193 FROM action.all_circulation WHERE id = OLD.id;
197 $$ LANGUAGE 'plpgsql';
199 CREATE TRIGGER action_circulation_aging_tgr
200 BEFORE DELETE ON action.circulation
202 EXECUTE PROCEDURE action.age_circ_on_delete ();
205 CREATE OR REPLACE VIEW action.open_circulation AS
207 FROM action.circulation
208 WHERE checkin_time IS NULL
212 CREATE OR REPLACE VIEW action.billable_circulations AS
214 FROM action.circulation
215 WHERE xact_finish IS NULL;
217 CREATE VIEW stats.fleshed_circulation AS
219 CAST(c.xact_start AS DATE) AS start_date_day,
220 CAST(c.xact_finish AS DATE) AS finish_date_day,
221 DATE_TRUNC('hour', c.xact_start) AS start_date_hour,
222 DATE_TRUNC('hour', c.xact_finish) AS finish_date_hour,
223 cp.call_number_label,
228 FROM "action".circulation c
229 JOIN stats.fleshed_copy cp ON (cp.id = c.target_copy);
232 CREATE OR REPLACE FUNCTION action.circulation_claims_returned () RETURNS TRIGGER AS $$
234 IF OLD.stop_fines IS NULL OR OLD.stop_fines <> NEW.stop_fines THEN
235 IF NEW.stop_fines = 'CLAIMSRETURNED' THEN
236 UPDATE actor.usr SET claims_returned_count = claims_returned_count + 1 WHERE id = NEW.usr;
238 IF NEW.stop_fines = 'LOST' THEN
239 UPDATE asset.copy SET status = 3 WHERE id = NEW.target_copy;
244 $$ LANGUAGE 'plpgsql';
245 CREATE TRIGGER action_circulation_stop_fines_tgr
246 BEFORE UPDATE ON action.circulation
248 EXECUTE PROCEDURE action.circulation_claims_returned ();
250 CREATE TABLE action.hold_request_cancel_cause (
251 id SERIAL PRIMARY KEY,
254 INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (1,'Untargeted expiration');
255 INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (2,'Hold Shelf expiration');
256 INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (3,'Patron via phone');
257 INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (4,'Patron in person');
258 INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (5,'Staff forced');
259 INSERT INTO action.hold_request_cancel_cause (id,label) VALUES (6,'Patron via OPAC');
260 SELECT SETVAL('action.hold_request_cancel_cause_id_seq', 100);
262 CREATE TABLE action.hold_request (
263 id SERIAL PRIMARY KEY,
264 request_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
265 capture_time TIMESTAMP WITH TIME ZONE,
266 fulfillment_time TIMESTAMP WITH TIME ZONE,
267 checkin_time TIMESTAMP WITH TIME ZONE,
268 return_time TIMESTAMP WITH TIME ZONE,
269 prev_check_time TIMESTAMP WITH TIME ZONE,
270 expire_time TIMESTAMP WITH TIME ZONE,
271 cancel_time TIMESTAMP WITH TIME ZONE,
272 cancel_cause INT REFERENCES action.hold_request_cancel_cause (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
274 target BIGINT NOT NULL, -- see hold_type
275 current_copy BIGINT REFERENCES asset.copy (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED,
276 fulfillment_staff INT REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
277 fulfillment_lib INT REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
278 request_lib INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
279 requestor INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
280 usr INT NOT NULL REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
281 selection_ou INT NOT NULL,
282 selection_depth INT NOT NULL DEFAULT 0,
283 pickup_lib INT NOT NULL REFERENCES actor.org_unit DEFERRABLE INITIALLY DEFERRED,
284 hold_type TEXT NOT NULL CHECK (hold_type IN ('M','T','V','C')),
285 holdable_formats TEXT,
287 email_notify BOOL NOT NULL DEFAULT TRUE,
288 frozen BOOL NOT NULL DEFAULT FALSE,
289 thaw_date TIMESTAMP WITH TIME ZONE
292 CREATE INDEX hold_request_target_idx ON action.hold_request (target);
293 CREATE INDEX hold_request_usr_idx ON action.hold_request (usr);
294 CREATE INDEX hold_request_pickup_lib_idx ON action.hold_request (pickup_lib);
295 CREATE INDEX hold_request_current_copy_idx ON action.hold_request (current_copy);
296 CREATE INDEX hold_request_prev_check_time_idx ON action.hold_request (prev_check_time);
299 CREATE TABLE action.hold_request_note (
301 id BIGSERIAL PRIMARY KEY,
302 hold BIGINT NOT NULL REFERENCES action.hold_request (id)
304 DEFERRABLE INITIALLY DEFERRED,
307 slip BOOL NOT NULL DEFAULT FALSE,
308 pub BOOL NOT NULL DEFAULT FALSE
311 CREATE INDEX ahrn_hold_idx ON action.hold_request_note (hold);
314 CREATE TABLE action.hold_notification (
315 id SERIAL PRIMARY KEY,
316 hold INT NOT NULL REFERENCES action.hold_request (id) DEFERRABLE INITIALLY DEFERRED,
317 notify_staff INT REFERENCES actor.usr (id) DEFERRABLE INITIALLY DEFERRED,
318 notify_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
319 method TEXT NOT NULL, -- email address or phone number
322 CREATE INDEX ahn_hold_idx ON action.hold_notification (hold);
324 CREATE TABLE action.hold_copy_map (
325 id SERIAL PRIMARY KEY,
326 hold INT NOT NULL REFERENCES action.hold_request (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
327 target_copy BIGINT NOT NULL REFERENCES asset.copy (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
328 CONSTRAINT copy_once_per_hold UNIQUE (hold,target_copy)
330 -- CREATE INDEX acm_hold_idx ON action.hold_copy_map (hold);
331 CREATE INDEX acm_copy_idx ON action.hold_copy_map (target_copy);
333 CREATE TABLE action.transit_copy (
334 id SERIAL PRIMARY KEY,
335 source_send_time TIMESTAMP WITH TIME ZONE,
336 dest_recv_time TIMESTAMP WITH TIME ZONE,
337 target_copy BIGINT NOT NULL REFERENCES asset.copy (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED,
338 source INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
339 dest INT NOT NULL REFERENCES actor.org_unit (id) DEFERRABLE INITIALLY DEFERRED,
340 prev_hop INT REFERENCES action.transit_copy (id) DEFERRABLE INITIALLY DEFERRED,
341 copy_status INT NOT NULL REFERENCES config.copy_status (id) DEFERRABLE INITIALLY DEFERRED,
342 persistant_transfer BOOL NOT NULL DEFAULT FALSE
344 CREATE INDEX active_transit_dest_idx ON "action".transit_copy (dest);
345 CREATE INDEX active_transit_source_idx ON "action".transit_copy (source);
346 CREATE INDEX active_transit_cp_idx ON "action".transit_copy (target_copy);
349 CREATE TABLE action.hold_transit_copy (
350 hold INT REFERENCES action.hold_request (id) ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED
351 ) INHERITS (action.transit_copy);
352 ALTER TABLE action.hold_transit_copy ADD PRIMARY KEY (id);
353 ALTER TABLE action.hold_transit_copy ADD CONSTRAINT ahtc_tc_fkey FOREIGN KEY (target_copy) REFERENCES asset.copy (id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED;
354 CREATE INDEX active_hold_transit_dest_idx ON "action".hold_transit_copy (dest);
355 CREATE INDEX active_hold_transit_source_idx ON "action".hold_transit_copy (source);
356 CREATE INDEX active_hold_transit_cp_idx ON "action".hold_transit_copy (target_copy);
359 CREATE TABLE action.unfulfilled_hold_list (
360 id BIGSERIAL PRIMARY KEY,
361 current_copy BIGINT NOT NULL,
363 circ_lib INT NOT NULL,
364 fail_time TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
367 CREATE OR REPLACE FUNCTION asset.merge_record_assets( target_record BIGINT, source_record BIGINT ) RETURNS INT AS $func$
369 moved_objects INT := 0;
370 source_cn asset.call_number%ROWTYPE;
371 target_cn asset.call_number%ROWTYPE;
372 metarec metabib.metarecord%ROWTYPE;
373 hold action.hold_request%ROWTYPE;
374 ser_rec serial.record_entry%ROWTYPE;
381 -- move any 856 entries on records that have at least one MARC-mapped URI entry
382 SELECT INTO uri_count COUNT(*)
383 FROM asset.uri_call_number_map m
384 JOIN asset.call_number cn ON (m.call_number = cn.id)
385 WHERE cn.record = source_record;
387 IF uri_count > 0 THEN
389 SELECT COUNT(*) INTO counter
396 ) as t(i int,c text);
398 FOR i IN 1 .. counter LOOP
399 SELECT '<datafield xmlns="http://www.loc.gov/MARC21/slim" tag="856">' ||
402 '<subfield code="' || subfield || '">' ||
405 regexp_replace(data,'&','&','g'),
411 ) || '</datafield>' INTO uri_datafield
415 'biblio.record_entry',
416 '//*[@tag="856"][position()=' || i || ']/*/@code|' ||
417 '//*[@tag="856"][position()=' || i || ']/*[@code]',
418 'id=' || source_record
419 ) as t(id int,subfield text,data text);
421 uri_text := uri_text || uri_datafield;
424 IF uri_text <> '' THEN
425 UPDATE biblio.record_entry
426 SET marc = regexp_replace(marc,'(</[^>]*record>)', uri_text || E'\\1')
427 WHERE id = target_record;
432 -- Find and move metarecords to the target record
433 SELECT INTO metarec *
434 FROM metabib.metarecord
435 WHERE master_record = source_record;
438 UPDATE metabib.metarecord
439 SET master_record = target_record,
441 WHERE id = metarec.id;
443 moved_objects := moved_objects + 1;
446 -- Find call numbers attached to the source ...
447 FOR source_cn IN SELECT * FROM asset.call_number WHERE record = source_record LOOP
449 SELECT INTO target_cn *
450 FROM asset.call_number
451 WHERE label = source_cn.label
452 AND owning_lib = source_cn.owning_lib
453 AND record = target_record;
455 -- ... and if there's a conflicting one on the target ...
458 -- ... move the copies to that, and ...
460 SET call_number = target_cn.id
461 WHERE call_number = source_cn.id;
463 -- ... move V holds to the move-target call number
464 FOR hold IN SELECT * FROM action.hold_request WHERE target = source_cn.id AND hold_type = 'V' LOOP
466 UPDATE action.hold_request
467 SET target = target_cn.id
470 moved_objects := moved_objects + 1;
475 -- ... just move the call number to the target record
476 UPDATE asset.call_number
477 SET record = target_record
478 WHERE id = source_cn.id;
481 moved_objects := moved_objects + 1;
484 -- Find T holds targeting the source record ...
485 FOR hold IN SELECT * FROM action.hold_request WHERE target = source_record AND hold_type = 'T' LOOP
487 -- ... and move them to the target record
488 UPDATE action.hold_request
489 SET target = target_record
492 moved_objects := moved_objects + 1;
495 -- Find serial records targeting the source record ...
496 FOR ser_rec IN SELECT * FROM serial.record_entry WHERE record = source_record LOOP
497 -- ... and move them to the target record
498 UPDATE serial.record_entry
499 SET record = target_record
500 WHERE id = ser_rec.id;
502 moved_objects := moved_objects + 1;
505 -- Finally, "delete" the source record
506 DELETE FROM biblio.record_entry WHERE id = source_record;
508 -- That's all, folks!
509 RETURN moved_objects;
511 $func$ LANGUAGE plpgsql;