fix(database): replace DO block with psql conditional for var substitution (#319) #320
No reviewers
Labels
No labels
domain:backend
domain:devops
domain:frontend
status:approved
status:in-progress
status:needs-fix
status:qa
type:bug
type:devops
type:feature
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
ldraney/pal-e-platform!320
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "319-fix-psql-var-substitution"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Replace the SQL Job's
DO $body$ ... $body$;block with psql client-side\gset+\ifconditional. Same idempotent CREATE-or-ALTER role semantics, but actually works at runtime.Why
After PR #318 fixed the
DO $$HCL escape issue, the next bug surfaced: psql's:'admin_pw'variable substitution does NOT work inside dollar-quoted blocks. psql treats$body$ ... $body$as a single literal token and skips substitution. Postgres receives literal:'admin_pw'token and rejects withsyntax error at or near ":" at character 164.Discovered when Ava ran
make tofu-applypost-#318. Verified manually by running the new psql\if/\gsetSQL directly against basketball-api postgres —admin_approle created successfully (kubectl exec ... psql ... \du admin_app).The two approaches (procedural DO blocks, psql client-side conditionals) each work alone. PR #304's design tried to combine them and hit this fundamental conflict.
Changes
terraform/modules/database/main.tflines 192-202 — replace theDO $body$ BEGIN IF NOT EXISTS ... ELSE ... END $body$;block with:Same idempotent semantics. No dollar-quoting. psql variable substitution works natively in regular SQL.
Test Plan
kubectl exec ... psql -iagainst basketball-api postgres on 2026-04-30 —admin_approle + grants applied successfullytofu apply -target=module.databaseJob pod completes Succeeded (idempotent — finds existing role, ALTERs to same password = no-op)==> admin_app role provisioned successfullyReview Checklist
story:admin-row-crud,arch:postgres)Related Notes
/), #318 (DOHCL escape)/, DO HCL escape). All would have been caught by pre-mergetofu applyto a real cluster. Reinforcesfeedback_tofu_validate_not_k8s_api.Closes #319
PR #320 Review
DOMAIN REVIEW
Stack: Terraform + kubernetes_job_v1 + psql heredoc (Bash + SQL).
The fix correctly identifies the root cause: psql's
:'var'substitution is suppressed inside dollar-quoted bodies because psql treats$body$...$body$as a single literal token. Switching to client-side\gset+\if/\else/\endifmoves the conditional out of the server-side procedural block and back into the psql client where variable substitution is performed pre-send. Same idempotent CREATE-or-ALTER semantics, no behavior change to GRANTs.Specifics verified:
$body$,BEGIN,END IF, orEXECUTE format(...)remnants.\gsetcorrectly capturesrole_existsfrom the SELECT EXISTS expression.\if :role_exists/\else/\endifmatches psql conditional syntax. Leading whitespace before backslash meta-commands is tolerated in modern psql.:'admin_pw'now appears only in ordinary SQL (ALTER/CREATE ROLE) where psql substitutes pre-flight — the original bug path.<<'SQL'(single-quoted) — shell variables are still passed via-v admin_pw="$${ADMIN_APP_PASSWORD}"rather than expanded inline. Correct.ON_ERROR_STOP=1preserved — failed conditional or grant still aborts the Job.BLOCKERS
None.
NITS
admin_app role provisioned successfully.SOP COMPLIANCE
319-fix-psql-var-substitution(issue-prefixed, kebab-case)Closes #319presentstory:admin-row-crud,arch:postgres)fix(database):)PROCESS OBSERVATIONS
This is the 4th sequential runtime fix in one bootstrap apply chain (salt master crash → label
/→ DO$$HCL escape → psql var substitution). Each one is a real bug, but the pattern reinforcesfeedback_tofu_validate_not_k8s_api:tofu validate+tofu plancannot catch runtime SQL/psql/k8s-API behavior. A pre-mergetofu applyagainst a real cluster (or ephemeral test cluster) would have caught all four. Worth surfacing as a discovered scope item: pre-merge apply gate for database-module changes, since the failure mode is "merge → 5min discovery → revert/forward-fix loop." Change-failure-rate signal for DORA.Cycle-time impact is small (single-line conceptual fix, +9/-9), but the cumulative tax across 4 PRs in one chain is non-trivial. Not blocking this PR — flagging for the Epilogue/discovered-scope ledger.
VERDICT: APPROVED