PostgreSQL starters, one command per page
Thirty-two steps for developers who know some SQL and have just been handed a Postgres database: the server and its client processes, createdb, databases holding schemas holding tables, the search path, psql and its backslash commands, BEGIN and COMMIT, savepoints, the MVCC snapshot model, VACUUM and the autovacuum daemon, EXPLAIN and EXPLAIN ANALYZE, the index types B-tree, GIN and BRIN, index-only scans, json and jsonb, roles and GRANT, and pg_dump with pg_restore, one command per page.
A diagram, the classic trap, and one command to go try this week. That's a page.
PostgreSQL starters, one command per page
Thirty-two steps for developers who know some SQL and have just been handed a Postgres database: the server and its client processes, createdb, databases holding schemas holding tables, the search path, psql and its backslash commands, BEGIN and COMMIT, savepoints, the MVCC snapshot model, VACUUM and the autovacuum daemon, EXPLAIN and EXPLAIN ANALYZE, the index types B-tree, GIN and BRIN, index-only scans, json and jsonb, roles and GRANT, and pg_dump with pg_restore, one command per page.
Set in Space Grotesk, Inter and JetBrains Mono (SIL Open Font License).
PostgreSQL behaviour checked against the PostgreSQL 18 official documentation (postgresql.org/docs/current, PostgreSQL 18.6, released 2026-08-13), fetched and read during this build: Tutorial chapters 1 and 3 (Architectural Fundamentals, Creating a Database, Transactions); DDL chapters 5.8 and 5.10 (Privileges, Schemas); Concurrency Control 13.1 (Introduction); Indexes 11.2 and 11.9 (Index Types, Index-Only Scans); Data Types 8.14 (JSON Types); Chapter 21 (Database Roles); Maintenance 24.1 (Routine Vacuuming); Backup and Restore 25.1 (SQL Dump); the SQL commands EXPLAIN, GRANT, VACUUM; and the psql, pg_dump and pg_restore reference pages. Concepts are named as the documentation names them. Teaching conventions (one command a day) are named as conventions. This book quotes no verbatim passages and is an independent guide not affiliated with or endorsed by the PostgreSQL Global Development Group. PostgreSQL is a trademark of the PostgreSQL Community Association of Canada.
General information only. Not professional advice; verify against your own PostgreSQL version and the current documentation.
Your purchase is for personal use only. You do not have redistribution rights: please do not share, resell, or republish this book or its pages.
© 2026 Steve Hodgkiss. All rights reserved. Personal use only; no redistribution rights.
Edition 1.0 · stevehodgkiss.net
Contents
The model
What Postgres is: a server process with one process per connection, databases holding schemas holding tables, the public schema and the search path, and psql, the terminal front door to all of it.
- 01One server, many clients
- 02Databases hold schemas
- 03psql, the front door
PostgreSQL uses a client/server model. A server process, which manages the database files, accepts connections from client applications and performs database actions on their behalf. The server program is called postgres. The server forks a new process for each connection; the supervisor stays running while client and server processes come and go. Per the PostgreSQL 18 documentation, 1.2 Architectural Fundamentals.
One server, many clients
Let's say you've just typed psql and you're in. Worth knowing what actually happened.
There was already a server process running, called postgres. It manages the database files. Your psql is a client: it connects, and the server forks a whole new process just for your connection. Clients come and go, even from another machine. The server stays.
psql is just a client. postgres is the house.
Connect with psql this week and run SELECT version();. You just spoke to the server.
A database contains one or more named schemas, which in turn contain tables. Schemas also contain other named objects, including data types and functions. Within one schema, two objects of the same type cannot have the same name; the same name can be used in different schemas without conflict. Each database contains a public schema and a pg_catalog schema holding the system tables and built-in types, functions and operators. Per the PostgreSQL 18 documentation, 5.10 Schemas.
Databases hold schemas
Let's say you came from MySQL and your database.table habit just broke. It breaks one level deeper here.
A database contains schemas, and schemas hold tables, types and functions. Every database starts with a public schema, where your tables land by default, and a pg_catalog schema for the system tables and built-ins. Schemas aren't walls: you can reach any one you have privileges on.
Database, then schema, then table. Three levels.
In psql this week, run SELECT schema_name FROM information_schema.schemata; and list the schemas in your database.
psql is a terminal-based front-end to PostgreSQL. It enables you to type in queries interactively, issue them to the server and see the results. Input can be from a file or command line arguments. psql provides a number of meta-commands and various shell-like features for scripting. Per the PostgreSQL 18 documentation, the psql reference page.
psql, the front door
Let's say you've been pasting SQL into a GUI and someone hands you a server with only a terminal.
psql is the terminal front-end: you type queries, the server runs them, you see results. But psql speaks two languages. SQL goes to the server. Backslash meta-commands, psql keeps for itself, for navigating and scripting. -c runs one command string and exits. -f reads commands from a file. One rule from the docs: you can't mix SQL and meta-commands in a single -c, because -c must be completely parsable by the server.
SQL to the server. Backslash to psql.
Run psql -c 'SELECT 1;' this week: one command, straight in, straight out.
Safe changes
Bundling steps into all-or-nothing work: BEGIN and COMMIT, ROLLBACK, savepoints for part-way mistakes, and the snapshot model underneath it where readers never block writers.
- 01BEGIN and COMMIT
- 02Reading never blocks writing
A transaction bundles multiple steps into a single all-or-nothing operation. In PostgreSQL, a transaction is set up by surrounding the SQL commands with BEGIN and COMMIT. The intermediate states are not visible to other concurrent transactions, and if a failure prevents completion, none of the steps affect the database at all. Without an explicit BEGIN, each statement gets an implicit BEGIN and COMMIT wrapped around it. Per the PostgreSQL 18 documentation, 3.4 Transactions.
BEGIN and COMMIT
Let's say the migration takes money from one row and adds it to another, and only one of those may ever happen.
Wrap the steps in BEGIN and COMMIT and they become one transaction: atomic. Other sessions can't see the middle, and if anything fails partway, none of it lands. Without a BEGIN, every statement is its own tiny transaction; the docs say each gets an implicit BEGIN and COMMIT. The trap is quiet: open a transaction, forget the COMMIT, close the window. The work vanishes with it.
Either all of it lands, or none of it does.
In psql this week: BEGIN;, run one UPDATE, then quit with \q before COMMIT. Reconnect and check that nothing happened.
PostgreSQL maintains data consistency by using a multiversion model, Multiversion Concurrency Control, MVCC. Each SQL statement sees a snapshot of data as it was some time ago, regardless of the current state of the underlying data. Locks acquired for reading do not conflict with locks acquired for writing: reading never blocks writing and writing never blocks reading. Per the PostgreSQL 18 documentation, 13.1 Introduction.
Reading never blocks writing
Let's say the nightly report runs for an hour and you've been told never to write during it. In Postgres, write anyway.
Consistency comes from MVCC: each statement sees a snapshot of the data as it was some time ago, not a lock on the present. Readers read their snapshot; writers add new versions; neither waits. Reading never blocks writing, and writing never blocks reading.
Reading never blocks writing. That's the model.
In one psql session this week run BEGIN; then a SELECT, leave it open, and INSERT from a second session. Both work.
All indexes in PostgreSQL are secondary indexes, stored separately from the table's main data area, which is called the table's heap. In an ordinary index scan, each row retrieval requires fetching data from both the index and the heap, and the heap-access portion involves random access, which can be slow. Per the PostgreSQL 18 documentation, 11.9 Index-Only Scans and Covering Indexes.
The heap and the index
Let's say you added an index and the query still reads a lot. This is the picture that explains it.
Every index in Postgres is secondary: stored separately from the table's data area, which the docs call the heap. An index entry doesn't hold your row; it points at it. So an ordinary index scan fetches from both worlds, a walk down the index, then a lookup in the heap for each match. The docs call that heap portion a lot of random access, and it can be slow. That's also why the planner sometimes ignores your index: for enough matches, walking the heap directly is cheaper.
The index is a signpost. The heap is the city.
EXPLAIN a query that filters on an indexed column this week and find Index Scan using, your index's name, in the plan.
The whole model in one place: a server process managing databases that contain schemas and tables, one process per connection, MVCC snapshots where reading never blocks writing, updates leaving old row versions for VACUUM, secondary indexes beside the heap, roles with privileges, and pg_dump for the copy that outlives the machine. Per the PostgreSQL 18 documentation, chapters 1, 5, 13, 21, 24 and 25.
One cluster, one tree
Let's say someone asks what Postgres is, and you get one sentence.
One server, postgres, holding databases; each database holds schemas, and schemas hold tables, with indexes standing beside the heap like signposts beside a city. Every connection gets its own process. Every statement reads a snapshot, so readers never block writers, and VACUUM sweeps what updates leave behind. Roles own the objects, GRANT opens the doors, pg_dump keeps the copy that outlives the machine.
One server, many databases, one tree.
Draw the tree from memory this week: server, database, schema, table, heap, index. Then check it against \l and \dt.