Skip to content

1.8 Step | CreateDatabase

Omisen edited this page Aug 14, 2026 · 2 revisions

Creates the application database (db_name) owned by db_user, reversibly. It lives in src/steps/create_database.rs. A port of create_db_if_missing from lib/postgres.sh.

This is where the most important protection in the whole installer lives.


Critical protection: never drop a pre-existing database

It is the single line of behaviour that separates “an installer that cleans up” from “an installer that can destroy a customer's data”.

A database with the same name that already existed may hold real data. The undo drops it only if we created it (PreState::CreatedByUs). On Preexisting the undo is strictly a NO-OP.

The branch is designed to be impossible to get wrong: the PreState governs the drop, and the call to dropdb is reachable only inside the CreatedByUs branch. There is no code path that leads to dropping a Preexisting database.

undo:
  if prestate != CreatedByUs → log "pre-existing DB, NOT removed" and RETURN
  otherwise                  → dropdb --if-exists --force

Life cycle

Phase Behaviour
snapshot does the database already exist? (SELECT 1 FROM pg_database WHERE datname = ...) → Preexisting/Untracked. This is the protection's source of truth
run Preexisting → skip. Absent → createdb --owner <user> <db>, then CreatedByUs. dry_run → log only
undo only CreatedByUsdropdb --if-exists --force (closes active connections). Preexisting → NO-OP with an explicit log line

The verdict this step reaches is also read by a later step: the filestore (1.12b SetupDataDir) may only be removed if the database was ours, so it reads the answer through the db_created_by_us channel in the Context and persists a copy of its own. Ownership of the data and ownership of the directory are two different questions.


Why it is tested directly and redundantly

This is not politeness: it is the anti-destruction barrier. The tests pin it down from several angles:

  • Preexisting database → the undo produces no DROP DATABASE (and no createdb);
  • absent database (CreatedByUs) → createdb in run, dropdb in undo;
  • Preexisting database → no dropdb even when undo is invoked repeatedly.

If a future refactor broke the protection, these tests would fail immediately.


Design notes

  • --force on dropdb terminates active connections before removing — only for databases of ours.
  • Best-effort undo.
  • Coordination with 1.7 CreateDbRole: this step runs after the role, so on rollback its undo (dropping the database) precedes dropping the role that owns it.

Clone this wiki locally