What do COMMIT and ROLLBACK actually do, and when does the database make the decision for you?
COMMIT makes everything the transaction has done permanent and visible to other transactions. ROLLBACK undoes everything back to the state at the start of the transaction or the last savepoint. Both end the transaction, and in both cases the locks it held are released.
The decision is made for you when you are in autocommit mode, which is the default. Every single statement is its own transaction: it commits as soon as it finishes successfully, and it rolls back on its own if it fails. That is why an UPDATE with a missing WHERE clause is committed the instant it runs — there is no open transaction to roll back, because the statement was its own transaction.
Beginning a transaction explicitly with BEGIN switches off autocommit for that block, and only the COMMIT or ROLLBACK decides the outcome. The exception is when the transaction is left open: most clients close the connection at the end of the session, and a transaction with no explicit outcome is rolled back automatically when the connection closes. SQLite behaves the same way — an uncommitted transaction is discarded when the session ends.
Two practical consequences. First, a client that crashes mid-transaction leaves the work uncommitted, which is the safe default: nothing partial survives. Second, a transaction left open by a bug holds its locks until the connection is reaped, which is why production systems set a statement timeout and an idle-in-transaction timeout — PostgreSQL has both, and the second one is specifically designed to kill sessions that have been idle inside a transaction for too long.
The habit worth forming is to make the transaction boundary explicit at the top of the unit of work and to commit as soon as that work is done. A long-running transaction that spans a web request is holding locks on data that other people need to write to.