A small knob on a very large door. default_table_access_method names the table access method — the storage engine, to use the word everyone actually means — that new tables get when their CREATE TABLE doesn’t say otherwise. The default is heap. The context is user. And for the overwhelming majority of clusters, the value has never been anything else, because for the overwhelming majority of clusters there has never been anything else to set it to. The parameter is interesting for what it implies: that PostgreSQL’s storage layer is pluggable at all.
The door
PostgreSQL 12 shipped the table access method interface, the product of a heavy refactoring (largely Andres Freund’s) that pried the executor’s fingers off the assumption that a table is a heap. Since then, “how tuples are physically stored, scanned, inserted, and vacuumed” has been a set of some forty-five callback functions behind a pg_am catalog entry, creatable with CREATE ACCESS METHOD and selectable per-table with CREATE TABLE ... USING whatever. It’s the closest thing PostgreSQL has to MySQL’s storage-engine system, and Michael Paquier’s writeup at the time ended with the right spirit: “This feature opens a lot of doors and possibilities, so have fun with it.”
What this parameter does is mundane by comparison: it supplies the USING clause when you don’t. New tables, new materialized views, CREATE TABLE AS and SELECT INTO — anything that creates table storage without naming an access method gets default_table_access_method. Since PostgreSQL 15 you can also rewrite an existing table into a different method with ALTER TABLE ... SET ACCESS METHOD, and since 17 a partitioned table can carry an access method that its future partitions inherit — which matters, because partitioned tables are exactly where mixed-storage designs (hot row-store partitions, cold columnar ones) want to live.
What’s actually behind the door
Here the story gets more sober. Heap remains the only table AM that ships with PostgreSQL, and something like 99% of the world runs nothing else. The API has a constraint that’s done a lot to keep it that way: an AM that wants to support modifications and indexes must address tuples by TID — block number plus item number — which quietly assumes a heap-shaped world and makes life hard for designs that aren’t. The first generation of would-be replacements, zheap and zstore, are dead. The survivors are worth knowing by name. Citus columnar uses the API for compressed column-oriented storage — append-friendly, scan-fast, update-hostile. OrioleDB, Alexander Korotkov’s undo-log engine with the explicit ambition of someday replacing heap, has spent years as an extension-plus-patches proposition, the patch set shrinking release by release as pieces land upstream, but not yet a pure CREATE EXTENSION away. And tde_heap, from Percona’s pg_tde, wraps heap with transparent data encryption — notable because its documentation is the one place you’ll routinely see ALTER SYSTEM SET default_table_access_method recommended in production, as the way to make every new table encrypted by default rather than trusting every developer to remember a USING clause. (There is also Paquier’s blackhole_am, which stores nothing and returns nothing, and exists to show how the API works. Resist the temptation.)
That pg_tde pattern is the parameter’s real job description: it is the deployment mechanism for a deliberate storage decision, not a tuning knob. If your organization has adopted an alternative AM — for encryption, for columnar analytics — setting the default cluster-wide or per-database is how you make the policy ambient instead of per-table folklore. Per-session works too: SET default_table_access_method = columnar before a migration script that creates fifty analytics tables beats editing fifty DDL statements. If you haven’t adopted one, the correct value is heap, untouched, and the parameter is a door you can admire closed — though given where the OrioleDB patch set is heading, it may not stay merely admirable for many more releases.