Changes between Initial Version and Version 1 of OtherTopics


Ignore:
Timestamp:
07/03/26 15:01:28 (8 days ago)
Author:
223091
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • OtherTopics

    v1 v1  
     1= Other Topics =
     2
     3This page documents the work completed for Phase P9 of the Room Reservation System project. The focus of this phase is performance analysis of complex SQL reports, proposed performance indexes, and security measures related to application and database access.
     4
     5The phase is based on the complex reports created in Phase P6 and the advanced database objects created in Phase P7.
     6
     7== SQL Performance ==
     8
     9The performance analysis was performed using PostgreSQL execution plans with:
     10
     11{{{
     12EXPLAIN (ANALYZE, BUFFERS)
     13}}}
     14
     15The goal was to compare the execution plans before and after creating indexes for the most important complex report queries. The tested reports are:
     16
     17* Report 1: Quarterly room utilization report
     18* Report 2: Quarterly equipment demand and stock risk report
     19
     20Because the test database contains a relatively small amount of sample data, the absolute execution time is very low. However, the analysis still shows which tables are scanned, which joins are performed, and which indexes are useful for larger datasets.
     21
     22== Proposed indexes ==
     23
     24The following indexes were proposed and created to improve filtering, joining, and ordering in the complex reports and application scenarios.
     25
     26{{{
     27CREATE INDEX IF NOT EXISTS idx_p9_reservations_room_date_status_time
     28ON project.reservations (room_id, reservation_date, status, start_time, end_time);
     29
     30CREATE INDEX IF NOT EXISTS idx_p9_reservations_date_status_id
     31ON project.reservations (reservation_date, status, reservation_id);
     32
     33CREATE INDEX IF NOT EXISTS idx_p9_rooms_building_id
     34ON project.rooms (building_id);
     35
     36CREATE INDEX IF NOT EXISTS idx_p9_reservation_equipment_reservation_id
     37ON project.reservation_equipment (reservation_id);
     38
     39CREATE INDEX IF NOT EXISTS idx_p9_reservation_equipment_equipment_id
     40ON project.reservation_equipment (equipment_id);
     41
     42CREATE INDEX IF NOT EXISTS idx_p9_room_equipment_equipment_id
     43ON project.room_equipment (equipment_id);
     44
     45CREATE INDEX IF NOT EXISTS idx_p9_approvals_reservation_id
     46ON project.approvals (reservation_id);
     47}}}
     48
     49The created indexes can be inspected in DBeaver.
     50
     51[[Image(p9_created_indexes.png, width=100%)]]
     52
     53''Figure: Created performance indexes for Phase P9.''
     54
     55=== Explanation of the proposed indexes ===
     56
     57The index ''idx_p9_reservations_room_date_status_time'' supports queries that search reservations by room, date, status, start time, and end time. This is useful for room availability checks and for detecting active room reservations in a selected time interval.
     58
     59The index ''idx_p9_reservations_date_status_id'' supports report queries that filter or group reservations by date and status. This is useful for quarterly reports and pending/approved reservation analysis.
     60
     61The index ''idx_p9_rooms_building_id'' supports joins between ''project.rooms'' and ''project.buildings''.
     62
     63The indexes on ''project.reservation_equipment'' support joins from reservations to requested equipment and from equipment to reservations.
     64
     65The index ''idx_p9_room_equipment_equipment_id'' supports queries that search which rooms contain a specific equipment type.
     66
     67The index ''idx_p9_approvals_reservation_id'' supports fast lookup of approval records for a reservation.
     68
     69== Report 1: Quarterly room utilization ==
     70
     71=== Report description ===
     72
     73The first complex report analyzes room usage by quarter. It shows room reservations grouped by quarter, building, room, room type, and capacity. It also calculates reservation counts by status, requested hours, approved hours, percentage share of approved room usage, utilization ranking, and utilization level.
     74
     75This report is useful for identifying which rooms are used most often and which rooms have lower utilization.
     76
     77=== SQL used for testing ===
     78
     79The report is executed through the view ''project.v_quarterly_room_utilization'', which was created in the previous phase.
     80
     81{{{
     82EXPLAIN (ANALYZE, BUFFERS)
     83SELECT *
     84FROM project.v_quarterly_room_utilization
     85ORDER BY quarter_start, utilization_rank, building_name, room_code;
     86}}}
     87
     88=== Execution plan before indexes ===
     89
     90Before creating the performance indexes, the report was executed with ''EXPLAIN (ANALYZE, BUFFERS)''. The execution plan shows the baseline performance of the query.
     91
     92[[Image(p9_report1_before_indexes.png, width=100%)]]
     93
     94''Figure: Execution plan for Report 1 before creating performance indexes.''
     95
     96=== Execution plan after indexes ===
     97
     98After creating the performance indexes, the same report was executed again with ''EXPLAIN (ANALYZE, BUFFERS)''.
     99
     100[[Image(p9_report1_after_indexes.png, width=100%)]]
     101
     102''Figure: Execution plan for Report 1 after creating performance indexes.''
     103
     104=== Performance discussion for Report 1 ===
     105
     106The first report reads data mainly from:
     107
     108* ''project.reservations''
     109* ''project.rooms''
     110* ''project.buildings''
     111
     112It groups reservations by quarter and room, calculates aggregate values, and ranks rooms by utilization.
     113
     114The most relevant indexes for this report are:
     115
     116* ''idx_p9_reservations_room_date_status_time''
     117* ''idx_p9_reservations_date_status_id''
     118* ''idx_p9_rooms_building_id''
     119
     120These indexes are useful because the report joins reservations with rooms and buildings, filters room-based reservations, and groups the results by date, room, and status.
     121
     122Since the dataset is small, PostgreSQL may still choose sequential scans or hash joins for some parts of the query because that can be cheaper for small tables. However, the indexes improve scalability and become more important when the number of reservations, rooms, and buildings increases.
     123
     124The execution plans before and after index creation were documented to show the difference in query planning and to verify that the report remains correct after adding indexes.
     125
     126== Report 2: Quarterly equipment demand and stock risk ==
     127
     128=== Report description ===
     129
     130The second complex report analyzes equipment demand by quarter. It compares the requested equipment quantities with the available general stock and the equipment assigned to rooms. The report helps identify equipment types with higher demand and possible stock risk.
     131
     132This report is useful for deciding whether additional equipment should be purchased or whether existing equipment is enough for future reservations.
     133
     134=== SQL used for testing ===
     135
     136{{{
     137EXPLAIN (ANALYZE, BUFFERS)
     138WITH room_stock AS (
     139SELECT
     140re.equipment_id,
     141SUM(re.quantity) AS assigned_room_quantity
     142FROM project.room_equipment re
     143GROUP BY re.equipment_id
     144),
     145equipment_demand AS (
     146SELECT
     147date_trunc('quarter', res.reservation_date)::date AS quarter_start,
     148e.equipment_id,
     149e.name AS equipment_name,
     150e.stock_quantity,
     151COALESCE(rs.assigned_room_quantity, 0) AS assigned_room_quantity,
     152e.stock_quantity + COALESCE(rs.assigned_room_quantity, 0) AS total_registered_quantity,
     153COUNT(DISTINCT res.reservation_id) AS reservations_with_equipment,
     154SUM(req.requested_quantity) AS total_requested_quantity,
     155SUM(req.requested_quantity) FILTER (WHERE res.status = 'approved') AS approved_requested_quantity,
     156SUM(req.requested_quantity) FILTER (WHERE res.status = 'pending') AS pending_requested_quantity,
     157SUM(req.requested_quantity) FILTER (WHERE res.status = 'rejected') AS rejected_requested_quantity
     158FROM project.reservation_equipment req
     159JOIN project.reservations res
     160ON req.reservation_id = res.reservation_id
     161JOIN project.equipment e
     162ON req.equipment_id = e.equipment_id
     163LEFT JOIN room_stock rs
     164ON e.equipment_id = rs.equipment_id
     165WHERE res.reservation_date >= DATE '2026-01-01'
     166AND res.reservation_date < DATE '2027-01-01'
     167GROUP BY
     168date_trunc('quarter', res.reservation_date)::date,
     169e.equipment_id,
     170e.name,
     171e.stock_quantity,
     172rs.assigned_room_quantity
     173)
     174SELECT
     175quarter_start,
     176equipment_name,
     177stock_quantity,
     178assigned_room_quantity,
     179total_registered_quantity,
     180reservations_with_equipment,
     181total_requested_quantity,
     182approved_requested_quantity,
     183pending_requested_quantity,
     184rejected_requested_quantity,
     185ROUND(total_requested_quantity::numeric / NULLIF(total_registered_quantity, 0), 2) AS demand_to_supply_ratio,
     186DENSE_RANK() OVER (
     187PARTITION BY quarter_start
     188ORDER BY total_requested_quantity DESC, equipment_name
     189) AS demand_rank,
     190CASE
     191WHEN total_requested_quantity > total_registered_quantity THEN 'stock_risk'
     192WHEN total_requested_quantity >= total_registered_quantity * 0.75 THEN 'high_demand'
     193WHEN total_requested_quantity > 0 THEN 'normal_demand'
     194ELSE 'no_demand'
     195END AS demand_level
     196FROM equipment_demand
     197ORDER BY quarter_start, demand_rank, equipment_name;
     198}}}
     199
     200=== Execution plan before indexes ===
     201
     202Before creating the performance indexes, the second report was executed with ''EXPLAIN (ANALYZE, BUFFERS)''.
     203
     204[[Image(p9_report2_before_indexes.png, width=100%)]]
     205
     206''Figure: Execution plan for Report 2 before creating performance indexes.''
     207
     208=== Execution plan after indexes ===
     209
     210After creating the performance indexes, the same report was executed again with ''EXPLAIN (ANALYZE, BUFFERS)''.
     211
     212[[Image(p9_report2_after_indexes.png, width=100%)]]
     213
     214''Figure: Execution plan for Report 2 after creating performance indexes.''
     215
     216=== Performance discussion for Report 2 ===
     217
     218The second report reads data mainly from:
     219
     220* ''project.reservation_equipment''
     221* ''project.reservations''
     222* ''project.equipment''
     223* ''project.room_equipment''
     224
     225The report joins reservation equipment with reservations and equipment, aggregates requested quantities, calculates total registered equipment quantity, and ranks equipment types by demand.
     226
     227The most relevant indexes for this report are:
     228
     229* ''idx_p9_reservations_date_status_id''
     230* ''idx_p9_reservation_equipment_reservation_id''
     231* ''idx_p9_reservation_equipment_equipment_id''
     232* ''idx_p9_room_equipment_equipment_id''
     233
     234These indexes are useful because the query joins reservation equipment to reservations and equipment, filters reservations by date, and aggregates equipment demand by equipment type.
     235
     236As with the first report, the sample database is small, so PostgreSQL may still choose sequential scans or hash joins where this is cheaper. The important point is that the indexes are now available for larger datasets and for selective searches on reservation dates, equipment identifiers, and reservation identifiers.
     237
     238== SQL performance conclusion ==
     239
     240The performance analysis shows that the complex reports work correctly before and after adding indexes. The indexes were selected based on the columns used in joins, filtering, grouping, and ordering.
     241
     242For a small test database, execution times are already low, so the performance gain may not be large. However, the proposed indexes are important for scalability because the same reports would become more expensive when the database contains many reservations, rooms, equipment records, and approval records.
     243
     244The most important indexed access patterns are:
     245
     246* searching reservations by room, date, status, and time interval;
     247* grouping reservations by date and status;
     248* joining rooms with buildings;
     249* joining reservations with requested equipment;
     250* joining equipment with room equipment;
     251* finding approvals by reservation.
     252
     253== Security measures ==
     254
     255This project includes security measures at both the application level and the database level.
     256
     257=== Application-level security measures ===
     258
     259The Java prototype uses JDBC ''PreparedStatement'' objects for SQL statements that receive user input. This prevents SQL injection because user input is passed as parameters instead of being concatenated directly into SQL strings.
     260
     261Examples of parameterized inputs include:
     262
     263* reservation date;
     264* start time and end time;
     265* selected room;
     266* selected user;
     267* selected equipment;
     268* approval decision;
     269* approval note.
     270
     271The database password is not hard-coded in the source code. The application asks for the database password when it starts. This reduces the risk of exposing credentials in the repository or documentation.
     272
     273The application connects to the database through the assigned SSH tunnel and uses the database credentials assigned through the EPRMS system.
     274
     275=== Database-level security measures ===
     276
     277The database also contains protection logic implemented in Phase P7:
     278
     279* only users with role ''admin'' or ''approver'' can approve or reject reservations;
     280* direct invalid approval inserts are rejected by a database trigger;
     281* approval decisions are restricted through the custom domain ''project.approval_decision_domain'';
     282* reservation overlap is prevented at database level;
     283* empty reservations without both room and equipment are rejected;
     284* requested equipment quantity is checked against available stock.
     285
     286These measures are important because they protect the database even when data is inserted directly through DBeaver or another database client, not only through the Java prototype.
     287
     288== Unauthorized approval rejection test ==
     289
     290To test database-level security, an unauthorized approval attempt was executed. The test tries to insert or create an approval using a user who does not have the required role.
     291
     292Example of an invalid approval attempt:
     293
     294{{{
     295INSERT INTO project.approvals (
     296reservation_id,
     297approver_id,
     298decision,
     299decision_time,
     300note
     301)
     302VALUES (
     3037,
     3041,
     305'approved',
     306CURRENT_TIMESTAMP,
     307'Unauthorized approval attempt.'
     308);
     309}}}
     310
     311In this example, user ID 1 is a regular user and is not allowed to approve reservations. The database trigger rejects the operation.
     312
     313[[Image(p9_unauthorized_approval_rejected.png, width=100%)]]
     314
     315''Figure: Unauthorized approval attempt rejected by the database security rule.''
     316
     317=== Security discussion ===
     318
     319The unauthorized approval test confirms that approval security is enforced inside the database. Even if someone tries to bypass the Java application and insert an approval directly into the ''project.approvals'' table, the database checks the role of the approver and rejects invalid writes.
     320
     321This is stronger than only checking the rule in the application, because the rule remains active for all database clients.
     322
     323== Other developments ==
     324
     325No additional optional topic was added in this phase. The main focus was performance analysis with indexes and security measures for application and database access.
     326
     327== Final conclusion ==
     328
     329Phase P9 improves the project in two important directions.
     330
     331First, the complex reports from Phase P6 were analyzed using PostgreSQL execution plans. Indexes were proposed and created for the most important filtering and join columns. The execution plans before and after index creation were documented with screenshots.
     332
     333Second, the project security was documented and tested. The application uses parameterized SQL statements and does not store the database password in the source code. The database enforces important security and consistency rules through triggers, stored functions, and a custom domain.
     334
     335Together, these additions make the Room Reservation System more scalable, more reliable, and better protected against invalid or unauthorized database operations.