Build Your Own PostgreSQL
Catalogs as the database about the database
What You Will Learn
- Understand metadata is data too in a PostgreSQL-like database
- Understand names are not enough in a PostgreSQL-like database
- Understand catalog map in a PostgreSQL-like database
- Understand the bootstrap knot in a PostgreSQL-like database
Metadata is data too
A relational database holds more than data. It also holds descriptions of that data. Tables have columns, columns have types, indexes belong to tables, functions accept argument types and return results, schemas contain objects, and roles hold privileges. The catalog is the database's internal record of all these facts. It describes the database to the database.
In a Postgre-like system, catalogs are themselves relations. That choice has real consequences. The engine can use its own storage, indexing, transaction, and query machinery to manage metadata. Creating a table inserts catalog rows. Dropping a function updates catalog state. Planning a query reads catalogs to resolve names and types. The system is self-hosting, because relational structures describe relational structures.
Names are not enough
Catalog entries need stable identities. Names can change, schemas can shift, and objects can be referenced by many other objects. Internal object identifiers let the system tell apart two tables that once shared a name at different times, or overloaded functions with the same name and different argument types. Human-readable names are part of the interface. Internal identities are what the engine trusts.
Catalog map
The bootstrap knot
Hard-coded metadata works until the database needs to change while it is running. A tiny database could keep table definitions in configuration files or in memory structures loaded at startup. That may be fine for a toy, but it makes transactional schema changes, permissions, dependency tracking, and introspection difficult. If metadata lives in transactional tables, a schema change can commit or roll back like any other change. Users can query system views to understand the database. Tools can inspect structure without private APIs.
The bootstrap problem is the catch. If catalogs are tables, how does the database know the definition of the catalog tables before the catalogs exist? A real system needs a bootstrap path: a small amount of compiled-in knowledge or initialization data that creates the first catalog structures. After that, the database can use its normal mechanisms. The system starts itself, then relies more and more on its own metadata.
Dependencies make change safe
Dependency tracking keeps schema evolution safe. A view may depend on a table column. An index depends on a table and operator class. A function may depend on a type. Dropping an object without accounting for its dependents can leave broken catalog references. The database must either reject the operation or cascade it deliberately. This is more than user convenience, because the executor and planner rely on catalog consistency.
Catalogs also make extensibility possible. If operators, types, functions, casts, and access methods are catalog entries, new behavior can be registered instead of hard-coded into every layer. The analyzer can resolve a user-defined operator by consulting catalogs. The planner can learn properties from metadata. The executor can call registered functions through a common interface. Catalog design decides how open the database can become without losing coherence.
Privileges live in catalog state too. Roles, memberships, grants, default privileges, and ownership rules must be durable and transactional. A query's meaning includes not only which object a name resolves to, but whether the current user may perform the requested operation. Security is therefore catalog-driven. Changing a grant changes future analysis and execution without rewriting application code.
Caching catalog lookups is essential. Query analysis and planning read metadata constantly. Fetching catalog rows from storage for every column and function reference would be expensive. A Postgre-like engine keeps system caches that map object identifiers and names to catalog tuples. Those caches must be invalidated when metadata changes. Here again the database needs coordination: fast cached reads plus a correct reaction to transactional DDL.
Schema changes are transactions with structural consequences. Adding a column, creating an index, or replacing a function modifies catalog rows and may trigger physical work. Other sessions may have plans that depend on old definitions. Locks, invalidation messages, and transaction visibility determine who sees which schema version and when. Catalogs are not static dictionaries. They are live data with concurrency rules.
Information schemas and system views present catalogs in user-friendly forms. Internal catalog tables may be optimized for engine needs rather than human readability. Views can translate them into portable or convenient shapes. This separation lets the engine keep precise metadata while tools and users get stable inspection surfaces.
Into extensibility
Catalogs close a loop. The database uses tables to describe tables, transactions to change metadata safely, indexes to find metadata quickly, and permissions to control metadata access. The next lesson covers the extensibility this design opens up: functions, operators, and custom behavior as ordinary parts of the database.
Once behavior is described as metadata, the engine can grow without turning every new idea into a core rewrite.