Hands-on lab exercise to learn PostgreSQL role management, GRANT and REVOKE permissions, role-based access control with practical examples using admin, teacher, and student roles.
By the end of this session, you will be able to:
CREATE DATABASE company_db;
\c company_db;
CREATE TABLE employees (
emp_id SERIAL PRIMARY KEY,
name VARCHAR(100),
salary NUMERIC,
department VARCHAR(50)
);
CREATE TABLE emp_audit (
audit_id SERIAL PRIMARY KEY,
action_type VARCHAR(20),
emp_id INT,
old_salary NUMERIC,
new_salary NUMERIC,
changed_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE statement_log (
log_id SERIAL PRIMARY KEY,
message TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO employees (name, salary, department)
VALUES
('Ali', 50000, 'IT'),
('Sara', 60000, 'HR'),
('Ahmed', 55000, 'Finance');
Prevent inserting employees with negative salary
CREATE OR REPLACE FUNCTION validate_salary()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.salary < 0 THEN
RAISE EXCEPTION 'Salary cannot be negative';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER before_insert_salary
BEFORE INSERT ON employees
FOR EACH ROW
EXECUTE FUNCTION validate_salary();
Track salary changes (audit system)
CREATE OR REPLACE FUNCTION log_salary_update()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO emp_audit(action_type, emp_id, old_salary, new_salary)
VALUES ('UPDATE', OLD.emp_id, OLD.salary, NEW.salary);
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER after_salary_update
AFTER UPDATE ON employees
FOR EACH ROW
EXECUTE FUNCTION log_salary_update();
UPDATE employees
SET salary = 70000
WHERE emp_id = 1;
Keep record of deleted employees
CREATE OR REPLACE FUNCTION log_employee_delete()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO emp_audit(action_type, emp_id, old_salary)
VALUES ('DELETE', OLD.emp_id, OLD.salary);
RETURN OLD;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER after_employee_delete
AFTER DELETE ON employees
FOR EACH ROW
EXECUTE FUNCTION log_employee_delete();
Prevent salary from being reduced below 30000
CREATE OR REPLACE FUNCTION prevent_low_salary()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.salary < 30000 THEN
RAISE EXCEPTION 'Salary cannot be less than 30000';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER before_salary_update
BEFORE UPDATE ON employees
FOR EACH ROW
EXECUTE FUNCTION prevent_low_salary();
Log bulk updates (runs once per query)
CREATE OR REPLACE FUNCTION log_bulk_operation()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO statement_log(message)
VALUES ('Bulk update executed on employees table');
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER after_bulk_update
AFTER UPDATE ON employees
FOR EACH STATEMENT
EXECUTE FUNCTION log_bulk_operation();
UPDATE employees
SET salary = salary + 1000;
π This will:
| Concept | Meaning |
|---|---|
NEW |
New values after INSERT/UPDATE |
OLD |
Previous values before UPDATE/DELETE |
FOR EACH ROW |
Runs for every affected row |
FOR EACH STATEMENT |
Runs once per SQL statement |
Create a trigger:
Create a trigger:
Create a trigger:
Modify audit table:
CURRENT_USER)CREATE TABLE accounts (
acc_id SERIAL PRIMARY KEY,
name VARCHAR(100),
balance NUMERIC
);
Create trigger to: