Lecture 10: DML, Triggers & Python DB-API
Modifying Data Safely (INSERT, UPDATE, DELETE, UPSERT), Triggers, Python sqlite3, and Preventing SQL Injection
Lecture Source Slide Deck
Original Slides: Lecture 10 (53 slides) · Amit Kumar Dhar (IIT Bhilai)
Download lecture10.pdf
1. Full Data Manipulation Language (DML)
1. INSERT Varieties
-- Single row insert with explicit columns
INSERT INTO student (id, name, dept_name, tot_cred)
VALUES ('98988', 'Tanaka', 'Biology', 0);
-- Multi-row insert
INSERT INTO department (dept_name, building, budget) VALUES
('Data Science', 'Alan Turing', 900000),
('Robotics', 'Grace Hopper', 750000);
-- Bulk insert from a query
INSERT INTO faculty_archive
SELECT * FROM instructor WHERE salary < 40000;2. UPDATE
-- Increase salaries of Comp. Sci. instructors by 5%
UPDATE instructor
SET salary = salary * 1.05
WHERE dept_name = 'Comp. Sci.';The Forgotten WHERE Clause
Executing UPDATE instructor SET salary = 100000; without a WHERE clause will overwrite every single row in the table! Always write the WHERE clause first.
3. DELETE vs. TRUNCATE vs. DROP
DELETE FROM t WHERE condition: Removes matching rows, fires row-level triggers, logs each row removal.TRUNCATE TABLE t: Deallocates data pages instantly, resets auto-increment sequences, bypasses row triggers, retains table schema.DROP TABLE t: Completely removes the table definition, constraints, and data from the catalog.
4. UPSERT (ON CONFLICT)
Modern databases (PostgreSQL, SQLite 3.24+) allow insert-or-update behavior atomically:
INSERT INTO course_views (course_id, view_count)
VALUES ('CS-101', 1)
ON CONFLICT (course_id) DO UPDATE SET
view_count = course_views.view_count + 1;2. Database Triggers
A Trigger is a procedural block of code stored in the database that is automatically executed ("fired") by the database engine whenever a specified event (INSERT, UPDATE, DELETE) occurs on a table.
Trigger Event: [ INSERT / UPDATE / DELETE ]
Timing: [ BEFORE | AFTER | INSTEAD OF ]
Granularity: [ FOR EACH ROW | FOR EACH STATEMENT ]Trigger Pseudorecords
OLD: Holds attribute values of the row before the modification (UPDATE,DELETE).NEW: Holds the prospective attribute values being inserted or updated (INSERT,UPDATE).
-- Example: Audit salary modifications into an audit log
CREATE TRIGGER audit_instructor_salary
AFTER UPDATE OF salary ON instructor
FOR EACH ROW
WHEN (OLD.salary <> NEW.salary)
BEGIN
INSERT INTO salary_log (instructor_id, old_sal, new_sal, changed_at)
VALUES (OLD.id, OLD.salary, NEW.salary, DATETIME('now'));
END;3. Python Database Programming (PEP 249 DB-API)
Applications communicate with relational databases via client drivers adhering to the Python DB-API standard:
import sqlite3
import sys
# 1. Establish connection
conn = sqlite3.connect("university.db")
# 2. Access columns by name like dictionary keys
conn.row_factory = sqlite3.Row
cur = conn.cursor()
# 3. Always enable foreign keys in SQLite
cur.execute("PRAGMA foreign_keys = ON;")
dept = sys.argv[1] if len(sys.argv) > 1 else "Comp. Sci."
# 4. Safe parameterized query (NEVER use f-strings or string concatenation!)
cur.execute("SELECT name, salary FROM instructor WHERE dept_name = ?", (dept,))
for row in cur.fetchall():
print(f"{row['name']}: ${row['salary']:,.2f}")
# 5. Commit mutations and close
conn.commit()
conn.close()4. SQL Injection Vulnerabilities & Prevention
The Catastrophic Flaw: String Interpolation
# VULNERABLE CODE: NEVER DO THIS!
user_input = "Comp. Sci.' OR '1'='1"
query = f"SELECT * FROM instructor WHERE dept_name = '{user_input}';"
cur.execute(query)The resulting executed query becomes:
SELECT * FROM instructor WHERE dept_name = 'Comp. Sci.' OR '1'='1';This bypasses authentication or leaks the entire table! If an attacker enters '; DROP TABLE student; --, entire databases can be erased.
The Solution: Parameterized Placeholders
With parameterized queries (? in SQLite, %s in Postgres), user input is sent as raw literal data in a separate protocol phase; the SQL compiler treats it strictly as a value, never executable code.
5. Topic Practice Questions
CREATE TRIGGER prevent_salary_reduction
BEFORE UPDATE OF salary ON instructor
FOR EACH ROW
WHEN (NEW.salary < OLD.salary)
BEGIN
SELECT RAISE(ABORT, 'Salary reduction is not permitted');
END;Answer:
Python's sqlite3 module manages transactions implicitly by default: it begins a transaction automatically before DML commands. If conn.commit() is not called before closing the connection or program exit, all changes will be automatically rolled back, leaving the database unchanged on disk.
Answer:
By default, cur.fetchall() returns tuples (e.g., ('Einstein', 95000.0)), requiring positional integer indexes (row[0]). Setting row_factory = sqlite3.Row wraps each row in a mapping that allows accessing columns by name (row['name']), enhancing code maintainability and robustness against schema modifications.
Lecture 09b: Window Functions & Analytical SQL
OVER Clause, PARTITION BY, Ranking Functions (ROW_NUMBER, RANK, DENSE_RANK), Window Frames, and Offset Functions (LEAD, LAG)
Lecture 11: Views, Functions & Database Security
Updatable Views, WITH CHECK OPTION, Materialized Views, Stored Logic, and RBAC (GRANT / REVOKE)