DatabaseProgramming: Phase4.sql

File Phase4.sql, 15.5 KB (added by 231504, 6 days ago)
Line 
1-- Phase 4(1)
2
3-- ============================================
4-- FUNCTION
5-- Calculate total money spent by a participant
6-- Improved with validation and realistic checks
7-- ============================================
8
9CREATE OR REPLACE FUNCTION get_total_participant_cost(
10 p_participant_id INT
11)
12 RETURNS DECIMAL(12, 2)
13AS
14$$
15DECLARE
16 total_flight DECIMAL(12, 2) := 0;
17 total_bus DECIMAL(12, 2) := 0;
18 total_hotel DECIMAL(12, 2) := 0;
19 total_cost DECIMAL(12, 2);
20 participant_exists INT;
21 participant_status VARCHAR(20);
22BEGIN
23
24 -- Check if participant exists
25 SELECT COUNT(*), MAX(status)
26 INTO participant_exists, participant_status
27 FROM trip_participants
28 WHERE id = p_participant_id;
29
30 IF participant_exists = 0 THEN
31 RAISE EXCEPTION
32 'Participant with ID % does not exist',
33 p_participant_id;
34 END IF;
35
36 -- Prevent calculating costs for cancelled participants
37 IF participant_status = 'cancelled' THEN
38 RAISE EXCEPTION
39 'Cannot calculate cost for cancelled participant %',
40 p_participant_id;
41 END IF;
42
43 -- Flight booking total
44 SELECT COALESCE(SUM(price), 0)
45 INTO total_flight
46 FROM flight_bookings
47 WHERE trip_participant_id = p_participant_id
48 AND price >= 0;
49
50 -- Bus booking total
51 SELECT COALESCE(SUM(price), 0)
52 INTO total_bus
53 FROM bus_bookings
54 WHERE trip_participant_id = p_participant_id
55 AND price >= 0;
56
57 -- Hotel booking total
58 SELECT COALESCE(SUM(hr.price), 0)
59 INTO total_hotel
60 FROM trip_participants_hotel_rooms tphr
61 JOIN hotel_rooms hr
62 ON hr.id = tphr.hotel_room_id
63 WHERE tphr.trip_participant_id = p_participant_id
64 AND hr.price >= 0;
65
66 total_cost :=
67 total_flight +
68 total_bus +
69 total_hotel;
70
71 RETURN ROUND(total_cost, 2);
72
73END;
74$$ LANGUAGE plpgsql;
75
76-- Example usage
77--SELECT get_total_participant_cost(5);
78
79-- Remove duplicated procedure
80DROP PROCEDURE IF EXISTS confirm_pending_participants;
81
82-- ============================================
83-- PROCEDURE
84-- Automatically confirm participants
85-- Improved with realistic passport validation
86-- ============================================
87
88CREATE OR REPLACE PROCEDURE confirm_trip_participants()
89AS
90$$
91BEGIN
92
93 UPDATE trip_participants
94 SET status = 'confirmed'
95 WHERE status = 'pending'
96
97 -- Passport must exist
98 AND passport_number IS NOT NULL
99
100 -- Remove empty spaces
101 AND LENGTH(TRIM(passport_number)) BETWEEN 6 AND 12
102
103 -- Prevent fake placeholder values
104 AND UPPER(TRIM(passport_number)) NOT IN (
105 'N/A',
106 'UNKNOWN',
107 'NONE',
108 '-'
109 )
110
111 -- Passport must contain only letters and numbers
112 AND passport_number ~ '^[A-Za-z0-9]+$'
113
114 -- Passport must contain at least 2 numbers
115 AND passport_number ~ '[0-9].*[0-9]'
116
117 -- Passport must contain at least 1 letter
118 AND passport_number ~ '[A-Za-z]';
119
120 -- Notify if no rows updated
121 IF NOT FOUND THEN
122 RAISE NOTICE
123 'No participants eligible for confirmation';
124 END IF;
125
126END;
127$$ LANGUAGE plpgsql;
128
129
130-- Example usage
131--CALL confirm_trip_participants();
132
133-- ============================================
134-- TRIGGER FUNCTION
135-- Prevent overbooking hotel rooms
136-- Improved with room existence checks
137-- ============================================
138
139CREATE OR REPLACE FUNCTION check_room_capacity()
140 RETURNS TRIGGER
141AS
142$$
143DECLARE
144 room_capacity INT;
145 current_count INT;
146BEGIN
147
148 -- Validate room exists + capacity
149 SELECT capacity
150 INTO room_capacity
151 FROM hotel_rooms
152 WHERE id = NEW.hotel_room_id;
153
154 IF room_capacity IS NULL THEN
155 RAISE EXCEPTION
156 'Hotel room with ID % does not exist',
157 NEW.hotel_room_id;
158 END IF;
159
160 IF room_capacity <= 0 THEN
161 RAISE EXCEPTION
162 'Invalid capacity for room ID %',
163 NEW.hotel_room_id;
164 END IF;
165
166 -- REALISTIC: count only overlapping bookings
167 SELECT COUNT(*)
168 INTO current_count
169 FROM trip_participants_hotel_rooms
170 WHERE hotel_room_id = NEW.hotel_room_id
171 AND NEW.check_in < check_out
172 AND NEW.check_out > check_in;
173
174 IF current_count >= room_capacity THEN
175 RAISE EXCEPTION
176 'Room capacity exceeded for room ID % in selected dates',
177 NEW.hotel_room_id;
178 END IF;
179
180 RETURN NEW;
181
182END;
183$$ LANGUAGE plpgsql;
184
185-- Create Trigger
186DROP TRIGGER IF EXISTS trg_check_room_capacity
187 ON trip_participants_hotel_rooms;
188
189CREATE TRIGGER trg_check_room_capacity
190 BEFORE INSERT
191 ON trip_participants_hotel_rooms
192 FOR EACH ROW
193EXECUTE FUNCTION check_room_capacity();
194
195-- ============================================
196-- FUNCTION
197-- Calculate trip execution score
198-- Improved with validation checks
199-- ============================================
200
201CREATE OR REPLACE FUNCTION get_trip_execution_score(
202 p_trip_execution_id INT
203)
204 RETURNS NUMERIC
205AS
206$$
207DECLARE
208 participant_count INT;
209 location_count INT;
210 avg_rating NUMERIC;
211 score NUMERIC;
212BEGIN
213
214 -- Validate trip execution existence
215 IF NOT EXISTS (SELECT 1
216 FROM trip_executions
217 WHERE id = p_trip_execution_id) THEN
218 RAISE EXCEPTION
219 'Trip execution with ID % does not exist',
220 p_trip_execution_id;
221 END IF;
222
223 SELECT tev.participant_count,
224 tev.visited_locations,
225 tev.average_rating
226 INTO
227 participant_count,
228 location_count,
229 avg_rating
230 FROM trip_execution_stats_view tev
231 WHERE tev.trip_execution_id = p_trip_execution_id;
232
233 score :=
234 COALESCE(participant_count, 0) * 2 +
235 COALESCE(location_count, 0) * 1.5 +
236 COALESCE(avg_rating, 0) * 10;
237
238 RETURN ROUND(score, 2);
239
240END;
241$$ LANGUAGE plpgsql;
242
243-- ============================================
244-- PROCEDURE
245-- Update user role dynamically
246-- Improved with role parameter and checks
247-- ============================================
248
249CREATE OR REPLACE PROCEDURE update_user_role(
250 p_user_id INT,
251 p_role_id INT DEFAULT NULL
252)
253AS
254$$
255DECLARE
256 trip_count INT;
257 user_exists INT;
258BEGIN
259
260 -- Validate user existence
261 SELECT COUNT(*)
262 INTO user_exists
263 FROM users
264 WHERE id = p_user_id;
265
266 IF user_exists = 0 THEN
267 RAISE EXCEPTION
268 'User with ID % does not exist',
269 p_user_id;
270 END IF;
271
272 -- Count trips
273 SELECT COUNT(*)
274 INTO trip_count
275 FROM trip_participants
276 WHERE user_id = p_user_id
277 AND status = 'confirmed';
278
279 -- Remove old roles
280 DELETE
281 FROM user_role_map
282 WHERE user_id = p_user_id;
283
284 -- Assign custom role if provided
285 IF p_role_id IS NOT NULL THEN
286
287 INSERT INTO user_role_map(user_id, role_id)
288 VALUES (p_user_id, p_role_id);
289
290 ELSE
291
292 -- Automatic role assignment
293 IF trip_count >= 5 THEN
294
295 INSERT INTO user_role_map(user_id, role_id)
296 VALUES (p_user_id, 2);
297
298 ELSE
299
300 INSERT INTO user_role_map(user_id, role_id)
301 VALUES (p_user_id, 1);
302
303 END IF;
304
305 END IF;
306
307END;
308$$ LANGUAGE plpgsql;
309
310-- ============================================
311-- TRIGGER FUNCTION
312-- Notify if trip execution already ended
313-- Improved with logical validations
314-- ============================================
315
316CREATE OR REPLACE FUNCTION auto_close_trip_execution()
317 RETURNS TRIGGER
318AS
319$$
320BEGIN
321
322 -- Validate dates
323 IF NEW.end_date < NEW.start_date THEN
324 RAISE EXCEPTION
325 'End date cannot be before start date';
326 END IF;
327
328 -- Notify if already ended
329 IF NEW.end_date < CURRENT_DATE THEN
330 RAISE NOTICE
331 'Trip execution % has already ended',
332 NEW.id;
333 END IF;
334
335 RETURN NEW;
336
337END;
338$$ LANGUAGE plpgsql;
339
340DROP TRIGGER IF EXISTS trg_auto_close_trip
341 ON trip_executions;
342
343CREATE TRIGGER trg_auto_close_trip
344 BEFORE UPDATE
345 ON trip_executions
346 FOR EACH ROW
347EXECUTE FUNCTION auto_close_trip_execution();
348
349-- ============================================
350-- FUNCTION
351-- Get user activity level
352-- Returns detailed table
353-- ============================================
354
355CREATE OR REPLACE FUNCTION get_user_activity_level(
356 p_user_id INT
357)
358 RETURNS TABLE
359 (
360 trip_count INT,
361 review_count INT,
362 total_activity INT
363 )
364AS
365$$
366BEGIN
367
368 -- Validate user existence
369 IF NOT EXISTS (SELECT 1
370 FROM users
371 WHERE id = p_user_id) THEN
372 RAISE EXCEPTION
373 'User with ID % does not exist',
374 p_user_id;
375 END IF;
376
377 RETURN QUERY
378 SELECT (SELECT COUNT(*)
379 FROM trip_participants
380 WHERE user_id = p_user_id)::INT,
381
382 (SELECT COUNT(*)
383 FROM trip_participants tp
384 JOIN reviews r
385 ON r.trip_participant_id = tp.id
386 WHERE tp.user_id = p_user_id)::INT,
387
388 (
389 (SELECT COUNT(*)
390 FROM trip_participants
391 WHERE user_id = p_user_id) +
392 (SELECT COUNT(*)
393 FROM trip_participants tp
394 JOIN reviews r
395 ON r.trip_participant_id = tp.id
396 WHERE tp.user_id = p_user_id)
397 )::INT;
398
399END;
400$$ LANGUAGE plpgsql;
401
402
403-- ============================================
404-- TRIGGER FUNCTION
405-- Validate participant status
406-- Improved with participant existence checks
407-- ============================================
408
409CREATE OR REPLACE FUNCTION validate_participant_status()
410 RETURNS TRIGGER
411AS
412$$
413BEGIN
414
415 IF NEW.status NOT IN (
416 'pending',
417 'confirmed',
418 'cancelled'
419 ) THEN
420 RAISE EXCEPTION
421 'Invalid participant status: %',
422 NEW.status;
423 END IF;
424
425 -- Prevent changing cancelled participants
426 IF OLD.status = 'cancelled'
427 AND NEW.status <> 'cancelled' THEN
428
429 RAISE EXCEPTION
430 'Cancelled participant status cannot be changed';
431
432 END IF;
433
434 RETURN NEW;
435
436END;
437$$ LANGUAGE plpgsql;
438
439DROP TRIGGER IF EXISTS trg_validate_participant_status
440 ON trip_participants;
441
442CREATE TRIGGER trg_validate_participant_status
443 BEFORE UPDATE
444 ON trip_participants
445 FOR EACH ROW
446EXECUTE FUNCTION validate_participant_status();
447
448-- ============================================
449-- FUNCTION
450-- Calculate trip duration
451-- Improved with validations
452-- ============================================
453
454CREATE OR REPLACE FUNCTION trip_duration_days(
455 p_trip_execution_id INT
456)
457 RETURNS INT
458AS
459$$
460DECLARE
461 days INT;
462BEGIN
463
464 -- Validate trip existence
465 IF NOT EXISTS (SELECT 1
466 FROM trip_executions
467 WHERE id = p_trip_execution_id) THEN
468 RAISE EXCEPTION
469 'Trip execution with ID % does not exist',
470 p_trip_execution_id;
471 END IF;
472
473 SELECT (end_date - start_date)
474 INTO days
475 FROM trip_executions
476 WHERE id = p_trip_execution_id;
477
478 IF days < 0 THEN
479 RAISE EXCEPTION
480 'Invalid trip dates';
481 END IF;
482
483 RETURN COALESCE(days, 0);
484
485END;
486$$ LANGUAGE plpgsql;
487
488-- ============================================
489-- PROCEDURE
490-- Assign hotel room to participant
491-- Improved with realistic validations
492-- ============================================
493
494CREATE OR REPLACE PROCEDURE assign_hotel_room(
495 p_trip_participant_id INT,
496 p_hotel_room_id INT,
497 p_check_in DATE,
498 p_check_out DATE
499)
500 LANGUAGE plpgsql
501AS
502$$
503BEGIN
504
505 -- Validate participant + status
506 IF NOT EXISTS (SELECT 1
507 FROM trip_participants
508 WHERE id = p_trip_participant_id
509 AND status = 'confirmed') THEN
510 RAISE EXCEPTION
511 'Participant does not exist or is not confirmed';
512 END IF;
513
514 -- Validate room
515 IF NOT EXISTS (SELECT 1
516 FROM hotel_rooms
517 WHERE id = p_hotel_room_id) THEN
518 RAISE EXCEPTION
519 'Hotel room does not exist';
520 END IF;
521
522 -- Prevent duplicate active booking
523 IF EXISTS (SELECT 1
524 FROM trip_participants_hotel_rooms
525 WHERE trip_participant_id = p_trip_participant_id
526 AND hotel_room_id = p_hotel_room_id
527 AND check_out >= CURRENT_DATE) THEN
528 RAISE EXCEPTION
529 'Participant already has an active booking for this room';
530 END IF;
531
532 INSERT INTO trip_participants_hotel_rooms(trip_participant_id,
533 hotel_room_id,
534 check_in,
535 check_out)
536 VALUES (p_trip_participant_id,
537 p_hotel_room_id,
538 p_check_in,
539 p_check_out);
540
541END;
542$$;
543
544-- ============================================
545-- TRIGGER FUNCTION
546-- Validate hotel booking dates
547-- Improved with realistic checks
548-- ============================================
549
550CREATE OR REPLACE FUNCTION validate_hotel_dates()
551 RETURNS TRIGGER
552AS
553$$
554BEGIN
555
556 -- Check date order
557 IF NEW.check_out <= NEW.check_in THEN
558 RAISE EXCEPTION
559 'Check-out date must be after check-in date';
560 END IF;
561
562 -- Prevent past reservations
563 IF NEW.check_in < CURRENT_DATE THEN
564 RAISE EXCEPTION
565 'Check-in date cannot be in the past';
566 END IF;
567
568 -- Limit unrealistic long stays
569 IF (NEW.check_out - NEW.check_in) > 60 THEN
570 RAISE EXCEPTION
571 'Hotel stay cannot exceed 60 days';
572 END IF;
573
574 RETURN NEW;
575
576END;
577$$ LANGUAGE plpgsql;
578
579DROP TRIGGER IF EXISTS trg_validate_hotel_dates
580 ON trip_participants_hotel_rooms;
581
582CREATE TRIGGER trg_validate_hotel_dates
583 BEFORE INSERT OR UPDATE
584 ON trip_participants_hotel_rooms
585 FOR EACH ROW
586EXECUTE FUNCTION validate_hotel_dates();
587
588-- ============================================
589-- PROCEDURE
590-- Cancel expired pending reservations
591-- ============================================
592
593CREATE OR REPLACE PROCEDURE cancel_expired_trip_reservations()
594AS
595$$
596BEGIN
597
598 UPDATE trip_participants tp
599 SET status = 'cancelled'
600 FROM trip_executions te
601 WHERE tp.trip_execution_id = te.id
602 AND tp.status = 'pending'
603
604 -- trip already finished (NOT just started)
605 AND te.end_date < CURRENT_DATE
606
607 -- invalid passport still blocks confirmation
608 AND (
609 tp.passport_number IS NULL
610 OR LENGTH(TRIM(tp.passport_number)) < 6
611 );
612
613 IF NOT FOUND THEN
614 RAISE NOTICE
615 'No expired reservations found';
616 ELSE
617 RAISE NOTICE
618 'Expired reservations successfully cancelled';
619 END IF;
620
621END;
622$$ LANGUAGE plpgsql;
623
624-- Example usage
625--CALL cancel_expired_trip_reservations();
626