Quiz 2

09 - SQL Functions, Procedures, Triggers & Security

1422 words
7 min read
Python Week 1: the first filter for runtime behavior
Visual companion
Python
Type and operator map

Python Week 1: the first filter for runtime behavior

View
Revision summary

What this note is really saying

Short form

# 09 - SQL Functions, Procedures, Triggers & Security ## 🎯 Learning Objectives After reading this topic, you will be able to: - Write SQL functions and procedures - Create row-level and statement-level triggers - Understand transition tables (OLD ROW/NEW ROW) - Use GRANT and REVOKE for access control - Explain SQL...

09 - SQL Functions, Procedures, Triggers & Security

🎯 Learning Objectives

After reading this topic, you will be able to:
  • Write SQL functions and procedures
  • Create row-level and statement-level triggers
  • Understand transition tables (OLD ROW/NEW ROW)
  • Use GRANT and REVOKE for access control
  • Explain SQL injection and how to prevent it

📋 Prerequisites

  • 07 - Advanced SQL — Subqueries, set operations, views
  • Understanding of database security concepts

📖 Core Content

9.1 Intuition: Making the Database Do More

SQL isn't just for queries. Modern databases support:
  • Functions: Computations that return a value
  • Procedures: Sequences of operations that modify data
  • Triggers: Automatic actions when data changes
  • Security: Controlling who can access what These features push logic closer to the data, improving performance and consistency.

9.2 Functions vs. Procedures

FeatureFunctionProcedure
ReturnsScalar value or relationNothing (or via OUT parameters)
Used inSELECT, WHERE clausesStandalone CALL / EXECUTE
Transaction controlNo COMMIT/ROLLBACKCan have COMMIT/ROLLBACK
Side effectsAvoid (should be pure)Expected

Functions

sql
CREATE FUNCTION dept_instructor_count(dept_name VARCHAR(20))
RETURNS INTEGER AS 

$$
DECLARE
    count INTEGER;
BEGIN
    SELECT COUNT(*) INTO count
    FROM instructor
    WHERE instructor.dept_name = dept_name;
    RETURN count;
END;
$$
 LANGUAGE plpgsql;
-- Using the function
SELECT dept_instructor_count('Comp. Sci.');

Procedures

sql
CREATE PROCEDURE give_raise(
    inst_id VARCHAR(5),
    amount NUMERIC(8,2)
)
LANGUAGE SQL
AS 

$$
UPDATE instructor
    SET salary = salary + amount
    WHERE ID = inst_id;
$$
;
-- Invoking the procedure
CALL give_raise('10101', 5000);

9.3 Triggers

A trigger automatically fires when a specified event (INSERT, UPDATE, DELETE) occurs on a table. (Diagram)

Row-Level Trigger

Fires once for each row affected:
sql
CREATE TRIGGER credits_earned
AFTER UPDATE OF grade ON takes
REFERENCING NEW ROW AS nrow
            OLD ROW AS orow
FOR EACH ROW
WHEN (nrow.grade <> 'F' AND nrow.grade IS NOT NULL
      AND (orow.grade = 'F' OR orow.grade IS NULL))
BEGIN ATOMIC
    UPDATE student
    SET tot_cred = tot_cred + (
        SELECT credits
        FROM course
        WHERE course.course_id = nrow.course_id
    )
    WHERE student.ID = nrow.ID;
END;
This trigger: When a grade is updated to a passing grade, add the course credits to the student's total credits.

Statement-Level Trigger

Fires once per statement, regardless of how many rows are affected:
sql
CREATE TRIGGER log_grade_changes
AFTER UPDATE OF grade ON takes
REFERENCING OLD TABLE AS ot OLD
            NEW TABLE AS nt NEW
FOR EACH STATEMENT
INSERT INTO grade_audit_log (timestamp, affected_rows)
VALUES (CURRENT_TIMESTAMP, (SELECT COUNT(*) FROM nt));
Trigger TypeFiresUse Case
Row-level (FOR EACH ROW)Once per affected rowValidating/modifying each row individually
Statement-level (FOR EACH STATEMENT)Once per SQL statementLogging, batch operations

Transition Tables

ReferenceMeaningAvailable In
OLD ROWThe row before modificationDELETE, UPDATE
NEW ROWThe row after modificationINSERT, UPDATE
OLD TABLEAll affected rows beforeDELETE, UPDATE (statement-level)
NEW TABLEAll affected rows afterINSERT, UPDATE (statement-level)

9.4 GRANT and REVOKE

GRANT

sql
GRANT SELECT ON instructor TO user1;
GRANT INSERT ON department TO user1, user2;
GRANT UPDATE (salary) ON instructor TO manager;  -- column-level!
GRANT ALL PRIVILEGES ON instructor TO user3;

REVOKE

sql
REVOKE SELECT ON instructor FROM user1;
REVOKE INSERT ON department FROM user1, user2;
REVOKE ALL PRIVILEGES ON instructor FROM user3;
PrivilegeMeaning
SELECTRead data
INSERTInsert new rows
UPDATEModify existing rows (can limit to specific columns)
DELETERemove rows
REFERENCESCreate foreign key references to the table
ALL PRIVILEGESAll of the above

Roles

A role is a collection of privileges that can be granted to users:
sql
CREATE ROLE instructor_role;
GRANT SELECT, UPDATE ON instructor TO instructor_role;
GRANT instructor_role TO user1, user2;

9.5 SQL Injection

SQL injection is a security vulnerability where an attacker inserts malicious SQL into a query:
python
# VULNERABLE code
def get_user(username):
    query = f"SELECT * FROM users WHERE name = '{username}'"
    # If username = "'; DROP TABLE users; --"
    # The query becomes:
    # SELECT * FROM users WHERE name = ''; DROP TABLE users; --'

Prevention

  1. Use parameterized queries (prepared statements):
python
# SAFE
cursor.execute("SELECT * FROM users WHERE name = %s", (username,))
  1. Validate input: Reject unexpected characters
  2. Limit privileges: Use read-only accounts where possible
  3. Use stored procedures: Pre-compiled SQL with parameters

9.6 Worked Examples

Example 1: Trigger to prevent salary reduction

sql
CREATE TRIGGER prevent_salary_cut
BEFORE UPDATE OF salary ON instructor
FOR EACH ROW
WHEN (NEW.salary < OLD.salary)
BEGIN
    RAISE EXCEPTION 'Salary cannot be reduced';
END;

Example 2: Grant read-only access

sql
CREATE ROLE read_only;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only;
GRANT read_only TO analyst1, analyst2;

📐 Key Formulas / Concepts

FeatureSyntaxBehavior
FunctionCREATE FUNCTION ... RETURNS ...Returns a value; can be used in queries
ProcedureCREATE PROCEDURE ...Performs actions; no return value
Row TriggerFOR EACH ROWFires per affected row
Statement TriggerFOR EACH STATEMENTFires once per SQL statement
Transition TableREFERENCING OLD/NEW ROW/TABLEAccesses before/after values

⚠️ Common Pitfalls

Pitfall 1: Triggers Causing Infinite Loops

The Mistake: A trigger on UPDATE performs an UPDATE on the same table, which fires the trigger again, which performs another UPDATE... Fix: Use WHEN conditions to avoid re-triggering, or use a different mechanism (CHECK constraint, application logic).

Pitfall 2: SQL Injection via String Concatenation

The Mistake: Building SQL queries by concatenating user input. Fix: Always use parameterized queries (prepared statements). The database separates SQL code from data.

Pitfall 3: Granting Too Many Privileges

The Mistake: GRANT ALL PRIVILEGES ON ALL TABLES TO public; Fix: Follow the principle of least privilege — grant only the minimum necessary access. Create specific roles for specific tasks.

📝 Practice Questions

Q1. What is the difference between a row-level trigger and a statement-level trigger?

Answer
  • Row-level (FOR EACH ROW): Fires once for each row affected by the triggering statement. Can access old and new values of each row.
  • Statement-level (FOR EACH STATEMENT): Fires once for the entire statement, regardless of how many rows are affected.
Example: An UPDATE affecting 100 rows fires the row trigger 100 times but the statement trigger once.

Q2. Write a GRANT statement that allows user1 to SELECT and UPDATE on the instructor table.

Answer
sql
GRANT SELECT, UPDATE ON instructor TO user1;
-- Or limit UPDATE to specific columns:
GRANT SELECT, UPDATE (name, dept_name) ON instructor TO user1;

Q3. What is SQL injection and how do you prevent it?

Answer
SQL injection: An attacker inserts malicious SQL code via user input that gets concatenated into a query. Example: Input ' OR 1=1; -- turns a login query into a query that returns all users.
Prevention:
  1. Use parameterized queries (prepared statements) — always
  2. Validate/sanitize user input
  3. Use least-privilege database accounts
  4. Use stored procedures with parameters

Q4. Write a function that returns the number of courses taught by a given instructor.

Answer
sql
CREATE FUNCTION course_count(instructor_id VARCHAR(5))
RETURNS INTEGER AS 
>DECLARE>countINTEGER;>BEGIN>SELECTCOUNT()INTOcount>FROMteaches>WHEREID=instructorid;>RETURNcount;>END;>> DECLARE > count INTEGER; > BEGIN > SELECT COUNT(*) INTO count > FROM teaches > WHERE ID = instructor_id; > RETURN count; > END; >
LANGUAGE plpgsql;
-- Usage: SELECT course_count('10101');

Q5. What is the purpose of a role in SQL security?

Answer
A role is a named collection of privileges that can be granted to multiple users. Benefits:
  • Centralized management: Change the role's privileges → all users in that role are updated
  • Consistency: No need to grant individual permissions
  • Organization: Match roles to job functions (manager, analyst, clerk)

Q6. Write a trigger that logs whenever a row is deleted from the instructor table.

Answer
sql
CREATE TRIGGER log_instructor_delete
AFTER DELETE ON instructor
REFERENCING OLD ROW AS o
FOR EACH ROW
INSERT INTO audit_log (event_type, table_name, record_id, timestamp)
VALUES ('DELETE', 'instructor', o.ID, CURRENT_TIMESTAMP);

Q7. What's the difference between a SQL function and a SQL procedure?

Answer
FeatureFunctionProcedure
Return valueYes (scalar or table)No (can have OUT params)
Use in SQLCan appear in SELECT/WHEREMust be called via CALL
Transaction controlNo COMMIT/ROLLBACKCan include COMMIT/ROLLBACK
Side effectsShould avoidExpected

Q8. What transition table references are available in different trigger events?

Answer
EventOLD ROWNEW ROWOLD TABLENEW TABLE
INSERTNoYesNoYes
UPDATEYesYesYesYes
DELETEYesNoYesNo

🔗 Cross-References

Document outline

Keep your place and jump directly to a heading.

Table of Contents
System Normal // Awaiting Context

Intelligence Hub

Navigate the knowledge graph to generate context. The Hub adapts dynamically to surface backlinks, related notes, and metadata insights.