Changes between Initial Version and Version 1 of AdvancedDatabaseDevelopment


Ignore:
Timestamp:
04/05/26 19:52:47 (6 days ago)
Author:
221511
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • AdvancedDatabaseDevelopment

    v1 v1  
     1= Advanced Database Development =
     2
     3== 1. Prevent Overlapping Reservations ==
     4
     5A resource cannot have two active reservations (approved, pending, or completed) that overlap in time. This business rule cannot be enforced with simple column-level constraints because it requires cross-row time-range overlap detection combined with status filtering.
     6
     7=== Implementation: Trigger ===
     8
     9{{{
     10CREATE OR REPLACE FUNCTION check_reservation_overlap()
     11RETURNS TRIGGER AS $$
     12BEGIN
     13    IF NEW.status IN ('cancelled', 'rejected') THEN
     14        RETURN NEW;
     15    END IF;
     16
     17    IF EXISTS (
     18        SELECT 1 FROM reservations r
     19        WHERE r.resource_id = NEW.resource_id
     20          AND r.reservation_id != NEW.reservation_id
     21          AND r.status IN ('approved', 'pending', 'completed')
     22          AND r.start_time < NEW.end_time
     23          AND r.end_time > NEW.start_time
     24    ) THEN
     25        RAISE EXCEPTION 'Reservation overlaps with an existing reservation for resource_id %',
     26            NEW.resource_id;
     27    END IF;
     28
     29    RETURN NEW;
     30END;
     31$$ LANGUAGE plpgsql;
     32
     33CREATE TRIGGER trg_check_reservation_overlap
     34    BEFORE INSERT OR UPDATE ON reservations
     35    FOR EACH ROW
     36    EXECUTE FUNCTION check_reservation_overlap();
     37}}}
     38
     39The trigger fires before every INSERT or UPDATE on the reservations table. It checks whether any existing active reservation (not cancelled or rejected) for the same resource has a time range that overlaps with the new reservation. Cancelled and rejected reservations are excluded from both the check and as subjects of the check, since they no longer occupy the resource.
     40
     41== 2. Enforce Resource Availability Window ==
     42
     43Reservations must respect the resource's daily availability hours ({{{available_from}}} / {{{available_to}}}) and weekend restrictions ({{{available_weekends}}}). For example, a classroom available 08:00-20:00 on weekdays should reject a reservation starting at 07:00 or on a Saturday.
     44
     45=== Implementation: Trigger ===
     46
     47{{{
     48CREATE OR REPLACE FUNCTION check_availability_window()
     49RETURNS TRIGGER AS $$
     50DECLARE
     51    v_resource RECORD;
     52BEGIN
     53    IF NEW.status IN ('cancelled', 'rejected') THEN
     54        RETURN NEW;
     55    END IF;
     56
     57    SELECT available_from, available_to, available_weekends
     58    INTO v_resource
     59    FROM resources
     60    WHERE resource_id = NEW.resource_id;
     61
     62    IF NOT v_resource.available_weekends
     63       AND EXTRACT(ISODOW FROM NEW.start_time) > 5 THEN
     64        RAISE EXCEPTION 'Resource (id: %) is not available on weekends', NEW.resource_id;
     65    END IF;
     66
     67    IF v_resource.available_from != TIME '00:00'
     68       OR v_resource.available_to != TIME '23:59' THEN
     69        IF NEW.start_time::TIME < v_resource.available_from THEN
     70            RAISE EXCEPTION 'Reservation starts at % which is before resource availability at %',
     71                NEW.start_time::TIME, v_resource.available_from;
     72        END IF;
     73        IF NEW.start_time::DATE = NEW.end_time::DATE
     74           AND NEW.end_time::TIME > v_resource.available_to THEN
     75            RAISE EXCEPTION 'Reservation ends at % which is after resource availability at %',
     76                NEW.end_time::TIME, v_resource.available_to;
     77        END IF;
     78    END IF;
     79
     80    RETURN NEW;
     81END;
     82$$ LANGUAGE plpgsql;
     83
     84CREATE TRIGGER trg_check_availability_window
     85    BEFORE INSERT OR UPDATE ON reservations
     86    FOR EACH ROW
     87    EXECUTE FUNCTION check_availability_window();
     88}}}
     89
     90The trigger looks up the resource's availability settings and validates the reservation times. Digital resources with 24-hour availability (00:00-23:59) skip the time-of-day check. The end-time check only applies to same-day reservations to correctly handle multi-day digital resource allocations (e.g., semester-long software licenses).
     91
     92== 3. Only Administrators Can Approve Reservations ==
     93
     94The {{{approved_by}}} field must reference a user with the Administrator role. This rule goes beyond basic foreign key integrity — it requires a cross-table role check that cannot be expressed as a simple FK constraint.
     95
     96=== Implementation: Trigger ===
     97
     98{{{
     99CREATE OR REPLACE FUNCTION check_approver_is_admin()
     100RETURNS TRIGGER AS $$
     101DECLARE
     102    v_type_name VARCHAR;
     103BEGIN
     104    IF NEW.approved_by IS NOT NULL THEN
     105        SELECT ut.type_name INTO v_type_name
     106        FROM users u
     107        JOIN user_types ut ON u.type_id = ut.type_id
     108        WHERE u.user_id = NEW.approved_by;
     109
     110        IF v_type_name IS NULL THEN
     111            RAISE EXCEPTION 'Approver user_id % does not exist', NEW.approved_by;
     112        END IF;
     113
     114        IF v_type_name != 'Administrator' THEN
     115            RAISE EXCEPTION 'Only administrators can approve reservations. User % is "%"',
     116                NEW.approved_by, v_type_name;
     117        END IF;
     118    END IF;
     119
     120    RETURN NEW;
     121END;
     122$$ LANGUAGE plpgsql;
     123
     124CREATE TRIGGER trg_check_approver_is_admin
     125    BEFORE INSERT OR UPDATE ON reservations
     126    FOR EACH ROW
     127    EXECUTE FUNCTION check_approver_is_admin();
     128}}}
     129
     130The trigger validates on every INSERT and UPDATE that if {{{approved_by}}} is set, the referenced user has the 'Administrator' type. This prevents students or teaching staff from being recorded as approvers.
     131
     132== 4. Auto-Complete Past Approved Reservations ==
     133
     134Approved reservations whose end time has passed should be automatically marked as completed. This procedure is intended to be called periodically (e.g., as a daily background job) to keep reservation statuses accurate without manual intervention.
     135
     136=== Implementation: Stored Procedure ===
     137
     138{{{
     139CREATE OR REPLACE PROCEDURE complete_past_reservations()
     140LANGUAGE plpgsql AS $$
     141DECLARE
     142    v_count INT;
     143BEGIN
     144    UPDATE reservations
     145    SET status = 'completed'
     146    WHERE status = 'approved'
     147      AND end_time < CURRENT_TIMESTAMP;
     148
     149    GET DIAGNOSTICS v_count = ROW_COUNT;
     150    RAISE NOTICE 'Completed % past approved reservations', v_count;
     151END;
     152$$;
     153}}}
     154
     155Usage: {{{CALL complete_past_reservations();}}}
     156
     157== 5. Auto-Reject Expired Pending Reservations ==
     158
     159Pending reservations whose start time has already passed without receiving approval should be automatically rejected. This prevents stale pending reservations from accumulating and keeps the system consistent.
     160
     161=== Implementation: Stored Procedure ===
     162
     163{{{
     164CREATE OR REPLACE PROCEDURE reject_expired_pending()
     165LANGUAGE plpgsql AS $$
     166DECLARE
     167    v_count INT;
     168BEGIN
     169    UPDATE reservations
     170    SET status = 'rejected'
     171    WHERE status = 'pending'
     172      AND start_time < CURRENT_TIMESTAMP;
     173
     174    GET DIAGNOSTICS v_count = ROW_COUNT;
     175    RAISE NOTICE 'Rejected % expired pending reservations', v_count;
     176END;
     177$$;
     178}}}
     179
     180Usage: {{{CALL reject_expired_pending();}}}
     181
     182== 6. Resource Utilization Dashboard ==
     183
     184An aggregated view showing reservation counts and total reserved hours per resource, broken down by status. Provides a quick overview for administrators to identify high-demand and underutilized resources without writing complex queries.
     185
     186=== Implementation: View ===
     187
     188{{{
     189CREATE OR REPLACE VIEW resource_utilization_dashboard AS
     190SELECT
     191    r.resource_id,
     192    r.name AS resource_name,
     193    rt.type_name AS resource_type,
     194    COALESCE(l.building || ' ' || l.room, 'Digital') AS location,
     195    COUNT(rv.reservation_id) AS total_reservations,
     196    COUNT(rv.reservation_id) FILTER (WHERE rv.status IN ('approved', 'completed'))
     197        AS approved_count,
     198    COUNT(rv.reservation_id) FILTER (WHERE rv.status = 'rejected')
     199        AS rejected_count,
     200    COUNT(rv.reservation_id) FILTER (WHERE rv.status = 'cancelled')
     201        AS cancelled_count,
     202    COUNT(rv.reservation_id) FILTER (WHERE rv.status = 'pending')
     203        AS pending_count,
     204    COALESCE(ROUND(SUM(EXTRACT(EPOCH FROM (rv.end_time - rv.start_time)) / 3600.0)
     205        FILTER (WHERE rv.status IN ('approved', 'completed')), 1), 0)
     206        AS total_reserved_hours
     207FROM resources r
     208JOIN resource_types rt ON r.type_id = rt.type_id
     209LEFT JOIN locations l ON r.location_id = l.location_id
     210LEFT JOIN reservations rv ON r.resource_id = rv.resource_id
     211GROUP BY r.resource_id, r.name, rt.type_name, l.building, l.room;
     212}}}
     213
     214Usage: {{{SELECT * FROM resource_utilization_dashboard ORDER BY total_reservations DESC;}}}
     215
     216== 7. Pending Reservations Overview ==
     217
     218A view listing all pending reservations with requester information and how many days each has been waiting for approval. Helps administrators prioritize which reservations to review first and identify bottlenecks in the approval process.
     219
     220=== Implementation: View ===
     221
     222{{{
     223CREATE OR REPLACE VIEW pending_reservations_overview AS
     224SELECT
     225    rv.reservation_id,
     226    u.first_name || ' ' || u.last_name AS requested_by,
     227    ut.type_name AS user_type,
     228    r.name AS resource_name,
     229    rv.start_time,
     230    rv.end_time,
     231    rv.purpose,
     232    rv.created_at,
     233    ROUND(EXTRACT(EPOCH FROM (CURRENT_TIMESTAMP - rv.created_at)) / 86400.0, 1)
     234        AS waiting_days
     235FROM reservations rv
     236JOIN users u ON rv.user_id = u.user_id
     237JOIN user_types ut ON u.type_id = ut.type_id
     238JOIN resources r ON rv.resource_id = r.resource_id
     239WHERE rv.status = 'pending'
     240ORDER BY rv.created_at;
     241}}}
     242
     243Usage: {{{SELECT * FROM pending_reservations_overview;}}}