1. Overview
Debugging PostgreSQL source code means attaching a debugger to a running database server process and tracing, at the code level, how a single SQL statement flows through the parser, planner, and executor. On Mac the default debugger is lldb, so the key setup is building PostgreSQL with debug symbols included and attaching to the backend process from VSCode.
TIP
As of the time of writing, version 17 is the latest release (excluding betas), so this article uses version 17 as the baseline.
The overall flow isn't much different from other operating systems (Linux, Windows), but on Mac you have to use lldb (Low Level Debugger, LLVM-based) instead of gdb, so a few settings and commands differ. By following the steps in this article, you'll be able to do the following yourself.
- Build the PostgreSQL source code in debug mode and run the server
- Attach to a PostgreSQL backend process with lldb from VSCode
- Set breakpoints and trace query execution flow at the code level
2. Setting Up the PostgreSQL Debugging Environment
2.1. Getting the PostgreSQL Source Code
PostgreSQL provides an official mirror on GitHub, but the actual upstream source is managed in a separate repository. So instead of GitHub, you should use the PostgreSQL's own Git repository.
git clone https://git.postgresql.org/git/postgresql.git
cd postgresqlAfter cloning, you're on the default master branch. To debug a specific version, checkout the corresponding branch/commit.
## 17 버전 STABLE checkout
git checkout REL_17_STABLE && git pull
## 특정 마이너 버전 checkout
git checkout REL_17_62.2. Building PostgreSQL
Install the packages required for the build.
brew install icu4c
brew install pkg-configFollowing the official PostgreSQL build procedure, run configure before building.
PG_VERSION=17
./configure \
--prefix=$HOME/postgres/pg${PG_VERSION} \
--enable-cassert \
--enable-debug CFLAGS="-ggdb -O0 -fno-omit-frame-pointer" CPPFLAGS="-g -O0" \
--with-includes=$(brew --prefix icu4c)/include \
--with-libraries=$(brew --prefix icu4c)/lib \
PKG_CONFIG_PATH=$(brew --prefix icu4c)/lib/pkgconfigOption breakdown:
--prefix: PostgreSQL installation path--enable-cassert: enables internalC Assertstatements--enable-debug: enables the debug buildCFLAGS:-ggdb(debug symbols),-O0(disables optimization),-fno-omit-frame-pointer(easier stack tracing)CPPFLAGS:-g(debug symbols)--with-includes/--with-libraries:icu4cheader/library pathsPKG_CONFIG_PATH:pkg-configlibrary search path
Once configure finishes, src/Makefile.global is generated. Check that the intended options (especially -g and -O0) actually made it in. If they're missing, you won't be able to see variable values while debugging — I learned this the hard way.
grep -E '^(CFLAGS|CPPFLAGS) = ' src/Makefile.globalCPPFLAGS = -isysroot $(PG_SYSROOT) -g -O0 -I/opt/homebrew/opt/icu4c@77/include
CFLAGS = -Wall ... -g -ggdb -O0 -fno-omit-frame-pointerNow build. The output lands in $HOME/postgres/pg17, the path specified with --prefix.
make && make installWARNING
The crucial part of the build is making sure -g -O0 is included in CFLAGS and CPPFLAGS. Without it, you cannot inspect variable values while debugging.
2.3. Running the PostgreSQL Server
Create PGDATA (the data directory) with initdb.
$HOME/postgres/pg17/bin/initdb -D $HOME/postgres/pgdata/pg17Before starting the server, check key settings such as port and max_connections in $HOME/postgres/pgdata/pg17/postgresql.conf. Now start the server and connect.
$HOME/postgres/pg17/bin/pg_ctl -D $HOME/postgres/pgdata/pg17 -l $HOME/postgres/pgdata/pg17/logfile start
## waiting for server to start.... done / server started
$HOME/postgres/pg17/bin/createdb -p 5432 test
$HOME/postgres/pg17/bin/psql -p 5432 testNOTE
Typing $HOME/postgres/pg17/bin every time is tedious. You could register it as an environment variable, but if you're debugging multiple PostgreSQL versions at once the paths can collide, so I don't recommend it.
3. Configuring the VSCode Debugging Environment
VS Code debugger settings can be applied per project or globally. This article uses the global settings approach.
NOTE
To configure it per project, create .vscode/launch.json in the source directory and add the contents below.
- Open the command palette with Command + Shift + P.
- Search for
settings.jsonand select Preferences: Open User Settings (JSON).
Opening settings.json
- Add the following to
settings.json.
View the VSCode global launch configuration
"launch": {
"version": "0.2.0",
"configurations": [
{
"name": "(lldb) Attach DB - PostgreSQL 17",
"type": "cppdbg",
"request": "attach",
"program": "${env:HOME}/postgres/pg${input:pg_version}/bin/postgres",
"MIMode": "lldb"
}
],
"inputs": [
{
"id": "pg_version",
"type": "promptString",
"description": "Enter the PostgreSQL Version",
"default": "17"
}
]
}- After connecting to the server, check the process PID with
SELECT pg_backend_pid();.
SELECT pg_backend_pid();
pg_backend_pid
----------------
85770
(1 row)- Launch the debugger with fn + F5 or Run and Debug → (lldb) Attach DB - PostgreSQL 17, then enter the version and the
pg_backend_pidvalue.
Running lldb attach
NOTE
The key part of the VSCode configuration is setting MIMode to lldb and request to attach so it connects to the running PostgreSQL backend process.
4. Debugging Test
- Find the query execution function
exec_simple_queryinsrc/backend/tcop/postgres.cand set a breakpoint.
exec_simple_query breakpoint
- Run a query from
psql.
CREATE TABLE test(id bigint not null generated by default as identity, name text);- You can confirm the breakpoint triggers as expected. From here you can debug your way through PostgreSQL and learn how it works at the code level.
Debugging screen paused at the triggered breakpoint
5. Wrapping Up
| Step | Key points |
|---|---|
| Preparing the source code | Clone from the official PostgreSQL Git repository and checkout the desired version |
| Debug build | Always include the -g -O0 options in configure (disables optimization) |
| Running the server | Create PGDATA with initdb, then start with pg_ctl |
| VSCode setup | Connect to the backend process PID in lldb · attach mode |
| Debugging | Set a breakpoint, run a query, and trace the code flow |
TIP
With a debugger you can learn how PostgreSQL works by tracing its internals directly at the code level as it executes.