Chapter 21 — Quiz
13 questions. Answers at the bottom.
Multiple choice
Q1. An audit trail is best populated by: - A) Application code only - B) A trigger (so no write path can bypass it) - C) Manual entry - D) A view
Q2. A soft delete:
- A) Physically removes the row
- B) Marks the row (e.g., deleted_at) instead of removing it
- C) Drops the table
- D) Truncates
Q3. The main risk of soft deletes is:
- A) Slower writes
- B) Forgetting WHERE deleted_at IS NULL somewhere, so deleted rows reappear
- C) Lost foreign keys
- D) Data is unrecoverable
Q4. The simplest, default hierarchy model is:
- A) Nested set
- B) Adjacency list (parent_id)
- C) Closure table
- D) Materialized path
Q5. Which hierarchy model has fast reads but expensive writes (renumbering)? - A) Adjacency list - B) Nested set - C) Closure table - D) Materialized path
Q6. A closure table stores: - A) Only direct parent links - B) All ancestor–descendant pairs (fast subtree reads) - C) A path string - D) Nothing
Q7. Tagging (item has many tags) is modeled with: - A) A comma-separated column - B) An array column always - C) A junction table (M:N) - D) A trigger
Q8. The polymorphic "type + id" columns approach: - A) Enforces foreign-key integrity - B) Cannot have a real foreign key (integrity not enforced) - C) Is always best - D) Requires nested sets
Q9. Shared-schema multi-tenancy (tenant_id column) without row-level security risks:
- A) Slow writes
- B) Cross-tenant data leakage (forgotten filter)
- C) Lost backups
- D) Nothing
Q10. Which multi-tenancy model gives the strongest isolation but most operational overhead? - A) Shared schema + tenant_id - B) Schema per tenant - C) Database per tenant - D) No tenancy
True/False
Q11. Storing audit before/after rows as jsonb lets one audit table serve any table. (True / False)
Q12. Nested sets are a great choice for a tree that is reorganized frequently. (True / False)
Short answer
Q13. A comment must attach to a post or a photo. Give one approach that preserves foreign-key integrity and one that sacrifices it, and state when the simpler one is acceptable.
---
Answer key
Q1 — B. A trigger fires for all write paths (app, admin, cascades) — completeness application code can't guarantee.
Q2 — B. Mark, don't remove (recoverable; preserves references/history).
Q3 — B. A forgotten filter leaks deleted rows; mitigate with a filtering view.
Q4 — B. Adjacency list (parent_id) — simplest; queried with recursive CTEs.
Q5 — B. Nested set: fast reads, painful writes (renumbering).
Q6 — B. All ancestor–descendant pairs → simple, fast subtree joins.
Q7 — C. M:N junction table. Never CSV; array only for small read-whole lists.
Q8 — B. type+id can't have a real FK (the id points to different tables), so integrity isn't enforced.
Q9 — B. A forgotten tenant_id filter leaks data across tenants; RLS enforces it at the DB.
Q10 — C. Database per tenant — strongest isolation, highest overhead.
Q11 — True. A jsonb old_row/new_row captures any table's whole row generically.
Q12 — False. Frequent reorganization makes nested-set writes (renumbering) very expensive; use adjacency list or closure table.
Q13. Integrity-preserving: separate junctions/FKs per type (post_comments, photo_comments) or a shared parent supertype both posts and photos reference, with comments pointing to it — real foreign keys. Integrity-sacrificing: a single comments(commentable_type, commentable_id) — one table, but no FK possible. The simpler type+id approach is acceptable when integrity is enforced elsewhere (app-level), the relationship is low-risk, and the convenience outweighs the lost referential guarantee — but choose it knowingly.
Scoring: 11–13 you've got the vocabulary; 8–10 review hierarchies and polymorphism; below 8, redo Exercises B–C.