Changes between Version 1 and Version 2 of P7


Ignore:
Timestamp:
01/18/26 00:42:10 (3 days ago)
Author:
193284
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • P7

    v1 v2  
    1 = Phase P7
    2 
    3 == Content
    4 To be defined.
     1= Phase P7: Advanced Database Development (Indexes, Constraints, Triggers, Views) =
     2
     3== Overview ==
     4In this phase we implement advanced database mechanisms that enforce correctness and improve performance in the Wedding Planner system.
     5Instead of relying only on application logic, we move important rules directly into PostgreSQL using indexes, constraints, triggers and views.
     6
     7== What we cover in this phase ==
     8 * Indexes for faster searching/filtering (availability checks, RSVP reports)
     9 * Constraints for preventing invalid/inconsistent data
     10 * Triggers for automation of derived logic (pricing, capacity checks)
     11 * Views for simplified reporting (RSVP summary, confirmed bookings overview)
     12
     13----
     14
     15== 1) Indexes (Query Performance) ==
     16Indexes speed up database queries by avoiding full table scans.
     17They are especially important in our system because availability checks and RSVP reports are executed frequently.
     18
     19=== Index 1: Event date/time search ===
     20This index improves performance when searching or filtering events by date + start/end time.
     21
     22*Table:* `event`
     23
     24{{{
     25CREATE INDEX idx_event_date_start ON event("date", start_time);
     26CREATE INDEX idx_event_date_end   ON event("date", end_time);
     27}}}
     28
     29*Why it matters:* 
     30When the system loads the schedule for a wedding day, the database can quickly find events in a time window (without scanning the whole table).
     31
     32=== Index 2: Booking status filtering (availability checks) ===
     33Availability endpoints frequently filter bookings by date + status (only CONFIRMED bookings should block resources).
     34
     35*Tables:* `venue_booking`, `photographer_booking`, `band_booking`
     36
     37{{{
     38CREATE INDEX idx_venue_booking_status        ON venue_booking("date", status);
     39CREATE INDEX idx_photographer_booking_status ON photographer_booking("date", status);
     40CREATE INDEX idx_band_booking_status         ON band_booking("date", status);
     41}}}
     42
     43*Reason:* 
     44These indexes make queries like “all confirmed bookings on date X” significantly faster.
     45
     46=== Index 3: RSVP status report ===
     47RSVP summaries filter guests by status per event.
     48
     49*Table:* `event_rsvp`
     50
     51{{{
     52CREATE INDEX idx_rsvp_event_status ON event_rsvp (event_id, status);
     53}}}
     54
     55*Reason:* 
     56This makes reporting queries efficient (e.g., confirmed vs declined guests for an event).
     57
     58----
     59
     60== 2) Constraints (Consistency & Integrity) ==
     61Constraints guarantee correctness even if invalid data reaches the database.
     62In Wedding Planner, this is critical for avoiding double bookings and enforcing valid status values.
     63
     64=== Constraint 1: Prevent overlapping bookings (NO double booking) ===
     65A venue / photographer / band must not be booked in overlapping time intervals.
     66
     67To support this, we first create generated timestamp columns and enable GiST indexing.
     68
     69{{{
     70CREATE EXTENSION IF NOT EXISTS btree_gist;
     71
     72ALTER TABLE venue_booking
     73ADD COLUMN start_ts TIMESTAMP NOT NULL GENERATED ALWAYS AS ("date" + start_time) STORED,
     74ADD COLUMN end_ts   TIMESTAMP NOT NULL GENERATED ALWAYS AS ("date" + end_time) STORED;
     75
     76ALTER TABLE photographer_booking
     77ADD COLUMN start_ts TIMESTAMP NOT NULL GENERATED ALWAYS AS ("date" + start_time) STORED,
     78ADD COLUMN end_ts   TIMESTAMP NOT NULL GENERATED ALWAYS AS ("date" + end_time) STORED;
     79
     80ALTER TABLE band_booking
     81ADD COLUMN start_ts TIMESTAMP NOT NULL GENERATED ALWAYS AS ("date" + start_time) STORED,
     82ADD COLUMN end_ts   TIMESTAMP NOT NULL GENERATED ALWAYS AS ("date" + end_time) STORED;
     83}}}
     84
     85Then we add EXCLUDE constraints:
     86
     87{{{
     88ALTER TABLE venue_booking
     89ADD CONSTRAINT exclude_overlap_vb EXCLUDE USING gist (
     90    venue_id WITH =,
     91    tsrange(start_ts, end_ts) WITH &&
     92);
     93
     94ALTER TABLE photographer_booking
     95ADD CONSTRAINT exclude_overlap_pb EXCLUDE USING gist (
     96    photographer_id WITH =,
     97    tsrange(start_ts, end_ts) WITH &&
     98);
     99
     100ALTER TABLE band_booking
     101ADD CONSTRAINT exclude_overlap_bb EXCLUDE USING gist (
     102    band_id WITH =,
     103    tsrange(start_ts, end_ts) WITH &&
     104);
     105}}}
     106
     107*Why this is important:* 
     108This enforces a real-world rule at database level:
     109even if two users try to insert bookings at the same time, PostgreSQL blocks conflicts automatically.
     110
     111=== Constraint 2: Status validation (CHECK constraints) ===
     112CHECK constraints restrict allowed values and prevent “random text statuses”.
     113
     114{{{
     115ALTER TABLE event
     116ADD CONSTRAINT chk_event_status
     117CHECK (status IN ('PLANNED', 'CONFIRMED', 'CANCELLED'));
     118
     119ALTER TABLE event_rsvp
     120ADD CONSTRAINT chk_event_rsvp_status
     121CHECK (status IN ('ACCEPTED', 'DECLINED', 'PENDING'));
     122
     123ALTER TABLE venue_booking
     124ADD CONSTRAINT chk_venue_booking_status
     125CHECK (status IN ('PENDING', 'CONFIRMED', 'CANCELLED'));
     126
     127ALTER TABLE photographer_booking
     128ADD CONSTRAINT chk_photographer_booking_status
     129CHECK (status IN ('PENDING', 'CONFIRMED', 'CANCELLED'));
     130
     131ALTER TABLE band_booking
     132ADD CONSTRAINT chk_band_booking_status
     133CHECK (status IN ('PENDING', 'CONFIRMED', 'CANCELLED'));
     134}}}
     135
     136Additional validations:
     137
     138{{{
     139ALTER TABLE wedding
     140ADD CONSTRAINT chk_budget CHECK (budget > 0);
     141
     142ALTER TABLE attendance
     143ADD CONSTRAINT chk_att_role
     144CHECK (role IN ('GUEST', 'BRIDE', 'GROOM', 'BEST_MAN', 'MAID_OF_HONOR'));
     145}}}
     146
     147*Result:* 
     148The database rejects invalid states automatically, ensuring consistent data across the whole system.
     149
     150
     151----
     152
     153== 3) Triggers (Automation of Business Logic) ==
     154Triggers automate derived logic and enforce rules without relying on the application.
     155
     156=== Trigger 1: Automatic venue booking price calculation ===
     157The total venue booking price is derived from:
     158`venue.price_per_guest * number_of_guests_for_wedding`.
     159
     160{{{
     161CREATE OR REPLACE FUNCTION calc_venue_price()
     162RETURNS trigger AS $$
     163BEGIN
     164    IF NEW.price IS NULL THEN
     165        SELECT price_per_guest * COUNT(*)
     166        INTO NEW.price
     167        FROM venue v
     168        JOIN guest g ON g.wedding_id = NEW.wedding_id
     169        WHERE v.venue_id = NEW.venue_id;
     170    END IF;
     171    RETURN NEW;
     172END;
     173$$ LANGUAGE plpgsql;
     174
     175CREATE TRIGGER trg_calc_venue_price
     176BEFORE INSERT ON venue_booking
     177FOR EACH ROW
     178EXECUTE FUNCTION calc_venue_price();
     179}}}
     180
     181*Why it matters:* 
     182This ensures consistent pricing and avoids manual calculation mistakes.
     183
     184=== Trigger 2: Venue capacity enforcement ===
     185This trigger prevents inserting guests if venue capacity would be exceeded.
     186
     187{{{
     188CREATE OR REPLACE FUNCTION check_guest_capacity()
     189RETURNS trigger AS $$
     190DECLARE
     191    guest_count INTEGER;
     192    venue_cap   INTEGER;
     193BEGIN
     194    SELECT v.capacity
     195    INTO venue_cap
     196    FROM venue_booking vb
     197    JOIN venue v ON v.venue_id = vb.venue_id
     198    WHERE vb.wedding_id = NEW.wedding_id
     199      AND vb.status = 'CONFIRMED';
     200
     201    IF venue_cap IS NULL THEN
     202        RETURN NEW;
     203    END IF;
     204
     205    SELECT COUNT(*) INTO guest_count
     206    FROM guest
     207    WHERE wedding_id = NEW.wedding_id
     208      AND (TG_OP = 'INSERT' OR guest_id <> OLD.guest_id);
     209
     210    IF guest_count + 1 > venue_cap THEN
     211        RAISE EXCEPTION 'Cannot add guest: venue capacity (%) exceeded', venue_cap;
     212    END IF;
     213
     214    RETURN NEW;
     215END;
     216$$ LANGUAGE plpgsql;
     217
     218CREATE TRIGGER trg_guest_capacity
     219BEFORE INSERT OR UPDATE ON guest
     220FOR EACH ROW
     221EXECUTE FUNCTION check_guest_capacity();
     222}}}
     223
     224*Why it matters:* 
     225It guarantees that capacity rules are enforced even if the app forgets to validate.
     226
     227----
     228
     229== 4) Views (Simplified Reports) ==
     230Views provide reusable report queries for the system dashboard.
     231
     232=== View 1: RSVP summary per event ===
     233{{{
     234CREATE VIEW vw_event_rsvp_summary AS
     235SELECT
     236    event_id,
     237    SUM(CASE WHEN status = 'ACCEPTED' THEN 1 ELSE 0 END) AS yes_count,
     238    SUM(CASE WHEN status = 'DECLINED' THEN 1 ELSE 0 END) AS no_count,
     239    SUM(CASE WHEN status = 'PENDING' THEN 1 ELSE 0 END) AS maybe_count
     240FROM event_rsvp
     241GROUP BY event_id;
     242}}}
     243
     244=== View 2: Confirmed bookings by date (all resources) ===
     245{{{
     246CREATE VIEW vw_confirmed_bookings_by_date AS
     247SELECT
     248    "date",
     249    'VENUE' AS booking_type,
     250    venue_id AS resource_id,
     251    start_time,
     252    end_time
     253FROM venue_booking
     254WHERE status = 'CONFIRMED'
     255
     256UNION ALL
     257
     258SELECT
     259    "date",
     260    'PHOTOGRAPHER' AS booking_type,
     261    photographer_id AS resource_id,
     262    start_time,
     263    end_time
     264FROM photographer_booking
     265WHERE status = 'CONFIRMED'
     266
     267UNION ALL
     268
     269SELECT
     270    "date",
     271    'BAND' AS booking_type,
     272    band_id AS resource_id,
     273    start_time,
     274    end_time
     275FROM band_booking
     276WHERE status = 'CONFIRMED';
     277}}}
     278
     279=== View 3: Guest count per wedding ===
     280{{{
     281CREATE VIEW vw_wedding_guest_count AS
     282SELECT
     283    wedding_id,
     284    COUNT(*) AS total_guests
     285FROM guest
     286GROUP BY wedding_id;
     287}}}
     288
     289----
     290
     291== Conclusion ==
     292This phase upgrades our PostgreSQL database with advanced mechanisms used in real systems.
     293Indexes improve performance for scheduling, booking checks and RSVP reports.
     294Constraints enforce correctness and prevent double bookings and invalid statuses.
     295Triggers automate business rules such as venue pricing and capacity limits.
     296Views simplify reporting and provide reusable queries for dashboards and analytics.
     297