目标描述 本教程的目标是配置一套 PostgreSql 的本地开发调试环境。
本教程所需的前置环境和目标环境为:
操作系统:Windows + WSL Ubuntu 22.04
IDE:VS Code
即,我们将在 Windows + WSL Ubuntu 22.04 的环境中,使用 VS Code IDE 进行 PostgreSql 的调试开发。
我们推荐将 PostgreSql 安装至 WSL 中,在 Windows 中使用 VSC Remote 连接 WSL 进行开发调试。因为 WSL 的配置非常简单,且能够直接在 Windows 系统上使用 Linux ,而 Linux 的环境配置非常简单;在 Windows 上使用 VSC 方便快捷,能够地减少学习 Linux 的学习成本。
配置步骤 由于我们的目标不是为开源社区做贡献,而是为了解决我们的个性化问题,因此追求最新版本的 PG 可能会得不偿失(最新版本的软件往往可能会出现较大的变更,不利于初步学习。如果你有研究成果开源化的需求,可以考虑使用最新版本的 PG 源码)。
在这里我选用了 v17.0 的 PG 源码进行学习, v17.0 是 PG 目前最后一个大版本更新,同时兼容我需要研究的插件,如果你想研究的插件或功能有特定版本需求,这一步需要明确你下载的源码版本。
1、克隆源码至 WSL 并使用 VSC Remote 打开项目 这一步可以根据需求选择克隆最新版本的 github 版本的源码,也可以考虑直接下载某个版本的源码。
1.1 github 如果你需要向社区提出贡献,那最好选择直接克隆指定版本,重度开发之后的分支冲突是不可接受的:
1 2 3 4 5 6 7 8 $ git clone https://github.com/postgres/postgres.git $ cd postgres $ git checkout -b FEATTURE-NAME $ EDIT YOUR CODE $ git commit -a $ git diff --patience master my-feature > ../my-feature.patch
1.2 官网版本目录 我们选择 17.0 版本直接下载,将下载的压缩包放置于 WSL,如下图:
1.3 使用 VSC Remote 打开任意 WSL 文件夹作为项目根目录: 接着,直接使用命令行或使用 VSC 远程连接 WSL 再打开命令行。这里只写如何用 VSC 连接 WSL 并打开文件夹和命令行:
然后执行解压命令:
1 tar -xf postgresql-17.0.tar.gz
接着,打开一个新的 VSC 窗口,重复上面的连接步骤,将解压的 PG 文件夹直接打开。
2、编译 2.1 VSC 插件安装 推荐安装:
2.2 pg 编译依赖库安装 pg 需要安装下列库,可以在这里直接输入命令行安装;也可以按照 2.4 章节的 VSC -> tasks ,在准备编译时一键安装。
1 sudo apt install -y libsystemd-dev libxml2-dev libssl-dev libicu-dev zlib1g-dev libreadline-dev pkg-config
2.3 编写 VSC 的编译配置文件 VS Code 中的任务 Task 可以配置为运行脚本和启动进程,以便可以在 VS Code 中使用许多现有工具,而无需输入命令行或编写新代码。工作区或文件夹特定任务是从工作区的 .vscode 文件夹中的 tasks.json 文件配置的。
我们需要打开 PG 源码根目录作为 VSC 的项目目录,然后 ctrl + shift + p 打开 VSC 的 Command Palette,然后按下图创建 Task 模板:
然后修改 tasks.json 为:
下面提供了两个配置文件,推荐使用后者。后者可以一键完成环境配置等任务。
2.3.1 官方简单示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 { "version" : "2.0.0" , "presentation" : { "reveal" : "always" } , "tasks" : [ { "label" : "Configure" , "type" : "shell" , "command" : "./configure --enable-depend --enable-cassert --enable-debug" , "problemMatcher" : [ "$eslint-compact" ] } , { "label" : "Make All" , "type" : "shell" , "command" : "make -j10 all" , "problemMatcher" : [ "$eslint-compact" ] } , { "label" : "Make Clean All" , "type" : "shell" , "command" : "make clean" , "problemMatcher" : [ "$eslint-compact" ] } , { "label" : "Make Install" , "type" : "shell" , "command" : "make install" } ] }
这个 JSON 摘取于 http://www.postgres.cn/v2/news/viewone/1/459 ,提供了四个任务的配置:
1、Configure 是编译前的配置文件生成
2、Make All 是编译全部代码
3、Make Clean All 是清空所以编译结果
4、Make Install 是编译并安装 PG
对于我们开发人员来说,一般只需要使用前三个 Task ,使用的步骤是:
1、在第一次 Make All 之前,执行一次 Configure 任务,之后无需执行 Configure 。
2、每次有代码变更后,执行 Make All 。如果遇到了不可解决的编译错误,尝试 Make Clean All 后重新 Make All。
执行任务的方法是:
打开 Command Palette ,选择 Tasks: Run Task,最后选择对应的 Configure、Make 或者 make install 命令来进行 PostgreSQL 的编译等任务。
2.3.2 比较完整的配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 { "tasks" : [ { "type" : "shell" , "label" : "install_depends" , "command" : "sudo apt install -y libsystemd-dev libxml2-dev libssl-dev libicu-dev zlib1g-dev libreadline-dev pkg-config" , "options" : { "cwd" : "${workspaceFolder}" } , "detail" : "Task install depends." } , { "type" : "shell" , "label" : "build_env" , "command" : "mkdir build && mkdir -p pgsql/data" , "options" : { "cwd" : "${workspaceFolder}" } , "detail" : "Task add folders." } , { "type" : "shell" , "label" : "build_config" , "command" : "../configure" , "args" : [ "--prefix=${workspaceFolder}/pgsql" , "--with-icu" , "--with-openssl" , "--with-systemd" , "--with-libxml" , "--enable-debug" ] , "options" : { "cwd" : "${workspaceFolder}/build" } , "detail" : "Task Build MakeFile." } , { "type" : "shell" , "label" : "make" , "command" : "make" , "args" : [ "-j12" ] , "options" : { "cwd" : "${workspaceFolder}/build" } , "detail" : "Task build." } , { "type" : "shell" , "label" : "make_install" , "command" : "make" , "args" : [ "install" ] , "options" : { "cwd" : "${workspaceFolder}/build" } , "detail" : "Task install database." } , { "type" : "shell" , "label" : "init_db" , "command" : "pgsql/bin/initdb" , "args" : [ "-D" , "pgsql/data" ] , "options" : { "cwd" : "${workspaceFolder}" } , "detail" : "Task init default database." } , { "type" : "shell" , "label" : "clean_db" , "command" : "make uninstall && make clean && rm -rf ../pgsql && rm -rf ../build" , "options" : { "cwd" : "${workspaceFolder}/build" } , "detail" : "Task clean database." } , { "type" : "shell" , "label" : "build_db_conf" , "dependsOn" : [ "build_env" , "build_config" ] , "dependsOrder" : "sequence" , "detail" : "Task add folders." } , { "type" : "shell" , "label" : "build_db" , "dependsOn" : [ "make" , "make_install" , "init_db" ] , "dependsOrder" : "sequence" , "detail" : "Task add folders." } , { "type" : "shell" , "label" : "rebuild_db" , "dependsOn" : [ "make" , "make_install" ] , "dependsOrder" : "sequence" , "detail" : "Task add folders." } , ] , "version" : "2.0.0" }
摘自 https://www.cnblogs.com/microestc/p/17079518.html
2.4 编译 我们在这里使用第二份 tasks.json 直接进行一系列的编译,经过实测后,该脚本还存在一些小问题。
1、我选用的 pg 版本还需要 bison flex 两个库。因此我修改了上述的 tasks.json 文件,添加了一些注释,并在安装依赖任务中添加了两个库的安装。
2、init_db 任务在系统第一次安装 pg 时还存在一些用户权限问题,pg 不推荐 root 用户进行初始化数据库,所以我选择了新建一个专门的 postgres 用户进行初始化 pg ,编写了对应的 task。并修改 init_db 这个 task 进行了适配。
2.4.1 新建 postgres 专用用户,将 PG 源码目录移动到该用户目录下 创建用户我提供了三种方法,选取其一就可以。我推荐第2个,直接在命令行中执行即可,比较简单。
1、编写创建用户脚本:
1 2 3 4 5 6 7 8 9 #!/bin/bash if ! id postgres &>/dev/null; then sudo useradd -m -s /bin/bash postgres echo "Postgres user created." else echo "Postgres user already exists." fi
2、手动检查并创建(推荐、自定义密码):
1 2 3 id postgressudo useradd -m -s /bin/bash postgressudo passwd postgres
3、使用我的 VSC task 直接创建:
2.4.2 章节的任务配置,让我们可以直接在 VSC 中以任务的形式创建 postgres 用户,按照直接执行 setup_postgres_user 任务即可。在 VSC 中执行 tasks.json 中编写的 task 的方法如下:
我们打开任意一个 WSL 目录作为 VSC 的项目根目录,按照 2.3 的教学创建 tasks.json 文件。然后在 VSC 中,按 ctrl + shift + p 打开 Command Palette ,创新新的 tasks 配置,然后将下面内容覆盖进去:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 { "version" : "2.0.0" , "tasks" : [ { "label" : "setup_postgres_user" , "type" : "shell" , "command" : "sudo" , "args" : [ "bash" , "-c" , "if ! id postgres &>/dev/null; then sudo useradd -m -s /bin/bash postgres; fi" ] , "detail" : "Create the postgres user and set up the necessary directories" } , ] }
接着 ctrl + shift + p 打开 Command Palette ,选择运行任务:
然后直接选择我们刚刚配置好的任务运行即可:
上述三个方法选一即可,建立完用户后,我们可以看到 home 目录下多出了一个 postgres 目录。这是 postgres 用户的个人目录。创建完了用户,记得把我们的 PG 压缩包移动到用户目录,然后解压!
1 tar -xf postgresql-17.0.tar.gz
如下图:
2.4.2 使用 postgres 用户编译并初始化 pg 这是我添加了注释和修改后的完整 tasks.json :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 { "tasks" : [ { "type" : "shell" , "label" : "install_depends" , "command" : "apt-get update && sudo apt install -y gcc libsystemd-dev libxml2-dev libssl-dev libicu-dev zlib1g-dev libreadline-dev pkg-config bison flex gdb lldb python3 python2 python3-lldb-14" , "options" : { "cwd" : "${workspaceFolder}" } , "detail" : "Task install depends." } , { "type" : "shell" , "label" : "build_env" , "command" : "sudo" , "args" : [ "bash" , "-c" , "mkdir -p ${workspaceFolder}/build && mkdir -p ${workspaceFolder}/build/data && chown -R postgres:postgres /home/postgres" ] , "options" : { "cwd" : "${workspaceFolder}" } , "detail" : "Task add folders." } , { "type" : "shell" , "label" : "build_config" , "command" : "../configure" , "args" : [ "--prefix=${workspaceFolder}/build" , "--with-icu" , "--with-openssl" , "--with-systemd" , "--with-libxml" , "--enable-debug" , "--enable-cassert" , "--enable-depend" , "CFLAGS= -ggdb -O0 -g3 -Wall -fno-omit-frame-pointer" ] , "options" : { "cwd" : "${workspaceFolder}/build" } , "detail" : "Task Build MakeFile." } , { "type" : "shell" , "label" : "make" , "command" : "make" , "args" : [ "-j12" ] , "options" : { "cwd" : "${workspaceFolder}/build" } , "detail" : "Task build." } , { "type" : "shell" , "label" : "make_install" , "command" : "make" , "args" : [ "install" ] , "options" : { "cwd" : "${workspaceFolder}/build" } , "detail" : "Task install database." } , { "label" : "init_db" , "type" : "shell" , "command" : "sudo" , "args" : [ "-u" , "postgres" , "build/bin/initdb" , "-D" , "build/data" ] , "options" : { "cwd" : "${workspaceFolder}" } , "detail" : "Initialize PostgreSQL database with initdb" } , { "type" : "shell" , "label" : "clean_db" , "command" : "make uninstall && make clean && rm -rf ../pgsql && rm -rf ../build" , "options" : { "cwd" : "${workspaceFolder}/build" } , "detail" : "Task clean database." } , { "type" : "shell" , "label" : "build_db_conf" , "dependsOn" : [ "build_env" , "build_config" ] , "dependsOrder" : "sequence" , "detail" : "Task add folders." } , { "type" : "shell" , "label" : "build_db" , "dependsOn" : [ "make" , "make_install" , "init_db" , "config_listen_port" , "add_env_to_postgres" , "start_or_restart_pg" ] , "dependsOrder" : "sequence" , "detail" : "Main task, build and start pg main program." } , { "type" : "shell" , "label" : "rebuild_db" , "dependsOn" : [ "make" , "make_install" , "start_or_restart_pg" ] , "dependsOrder" : "sequence" , "detail" : "Task add folders." } , { "label" : "switch_to_postgres" , "type" : "shell" , "command" : "sudo -u postgres bash -c 'echo Switched to postgres user.'" , "problemMatcher" : [ ] } , { "type" : "shell" , "label" : "config_listen_port" , "command" : "echo -e '\n# start listening ,config by dingning\nlisten_addresses = 'localhost'\nport = 5432' | sudo tee -a postgresql.conf" , "options" : { "cwd" : "${workspaceFolder}/build/data" } , "detail" : "Task config pg's listening port in data/postgresql.conf." } , { "type" : "shell" , "label" : "export_sys_env" , "command" : "sudo -u postgres sh -c 'export PATH=${workspaceFolder}/build/bin && export PGHOST=127.0.0.1 && export PGPORT=5432 && export LD_LIBRARY_PATH=${workspaceFolder}/build/lib'" , "options" : { "cwd" : "${workspaceFolder}/build/data" } , "detail" : "Task config pg's system environment variable." } , { "type" : "shell" , "label" : "add_env_to_postgres" , "command" : "sudo -u postgres bash -c \"echo -e '\\n# PostgreSQL environment variables\\nexport PATH=${workspaceFolder}/build/bin\\nexport PGHOST=127.0.0.1\\nexport PGPORT=5432\\nexport LD_LIBRARY_PATH=${workspaceFolder}/build/lib\\nexport PGHOME=${workspaceFolder}/build\\nexport PGDATA=${workspaceFolder}/build/data' >> ~/.bashrc && source ~/.bashrc\"" , "options" : { "cwd" : "${workspaceFolder}" } , "detail" : "Add PostgreSQL environment variables to the postgres user's .bashrc file." } , { "type" : "shell" , "label" : "start_or_restart_pg" , "command" : "sudo -u postgres sh -c 'build/bin/pg_ctl -D build/data -l ${workspaceFolder}/build/data/postmaster.log status > /dev/null 2>&1 && build/bin/pg_ctl -D pgsql/data -l ${workspaceFolder}/build/data/postmaster.log restart || build/bin/pg_ctl -D build/data -l ${workspaceFolder}/build/data/postmaster.log start'" , "options" : { "cwd" : "${workspaceFolder}" } , "detail" : "Start pg, if pg has been started, restart it." } ] , "version" : "2.0.0" }
你只需要按照 1.3 的步骤打开我们刚刚在 postgres 用户下解压的 PG 源码文件夹作为根目录,然后按照 2.3 的教学创建 tasks.json 文件,最后使用上面的这个 tasks.json 覆盖默认的 tasks.json 模板,即可使用这份 tasks.json。
安装依赖、编译安装、初始化 PG 的流程为下面三个任务,依次执行即可:
install_depends # 一次性任务,第一次安装时执行一次即可
build_db_conf # 生成 pg 构建配置文件,主要设置 编译要求 debug ,无代码优化,安装目录。
build_db # 编译构建 pg ,初始化 pg 到数据目录 build/data ,注册可执行文件的路径到用户的环境变量中
值得说一下的是,我写的这份 tasks.json 基本全覆盖了从依赖安装到编译、启动、重启、卸载的全流程任务。
build_db 任务将初始化 pg 到 build/data 目录,并将相关的依赖变量添加到用户 postgres 的环境变量中
执行完 build_db 任务,你就可以通过 createdb test 创建 test 数据库,通过 psql test 直接连接到 test 数据库 了
如需重新安装,只需要执行 clean_db 任务然后重新执行 build_db_conf 和 build_db 即可。
2.4.3 验证是否完成安装 执行完我们上述的 build_db 任务后,应当能够看到:
然后我们可以在命令行中直接尝试启动:
1 2 3 su - postgres cd postgresql-17.0pg_ctl -D pgsql/data -l logfile start
如下图,启动成功:
检查进程状态:
新建测试数据库并连接:
到这里,我们就完成了 pg 的编译和初始化,可以配置 debug 环境了!
3、调试 我推荐使用 GDB 进行调试,原因是 WSL 上由 apt 安装的 GDB 和 LLDB 都有问题,我只找了 GDB 的解决方案。
3.1 新建并编辑 调试配置文件 ctrl + shift + p ,然后选择 Debug: Open launch.json ,选择 lldb/gdb 。因为 PG 并没有使用 cmake ,所以我们直接选择 lldb/gdb 调试器即可:
然后使用下面的调试配置文件,推荐使用 GDB ,亲测 WSL 上直接使用 apt 安装的 lldb 也不能完成调试:
LLDB 1 2 3 4 5 6 7 8 9 10 11 12 { "version" : "0.2.0" , "configurations" : [ { "name" : "(CodeLLDB) Attach" , "type" : "lldb" , "request" : "attach" , "program" : "${workspaceFolder}/build/bin/postgres" , "pid" : "${command:pickProcess}" } ] }
GDB 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 { "version" : "0.2.0" , "configurations" : [ { "name" : "(gdb) 附加 pg" , "type" : "cppdbg" , "request" : "attach" , "program" : "${workspaceFolder}/build/bin/postgres" , "processId" : "${command:pickProcess}" , "MIMode" : "gdb" , "preLaunchTask" : "switch_to_postgres" , "setupCommands" : [ { "description" : "为 gdb 启用整齐打印" , "text" : "-enable-pretty-printing" , "ignoreFailures" : true } , { "description" : "将反汇编风格设置为 Intel" , "text" : "-gdb-set disassembly-flavor intel" , "ignoreFailures" : true } ] } ] }
3.2 安装调试器 gdb 从 apt 官方下载的 gdb 无法直接适配 WSL ,原因是 gdb 在读取程序内存区时使用了 /proc/PID/mem 文件,但 WSL1 和 WSL2 都不支持这种内存管理方式,如下图:
因此你需要额外下载安装 WSL 社区的修复版本 GDB:
1 2 3 4 5 6 7 8 9 cd /home/postgressudo apt remove gdbsudo apt install libgmp-devwget https://ftp.gnu.org/gnu/gdb/gdb-13.1.tar.xz tar -xf gdb-13.1.tar.xz cd gdb-13.1/./configure make -j8 sudo make install
https://github.com/microsoft/WSL/issues/8516
调试
这些程序是我们编译得到的可执行程序,我们可以通过 attach 进程的方式进行调试。
在调试之前,可以考虑将刚刚编译得到的 bin 程序目录使用下面的指令添加到 Linux 的环境变量中,这样可以直接使用这种格式的指令 psql 代替 postgresql-17.0 目录中使用 pgsql/bin/psql 指令。我已经在 task 的 build_db 任务里一键完成了环境变量的添加,如果你成功完成了 build_db 任务,那么这一步可以忽略。
1 2 3 export PATH=$PATH :/home/postgres/postgresql-17.0/pgsql/binexport PGHOST=127.0.0.1export PGPORT=5432
创建新数据库的程序是 pgsql/bin/createdb ,具体指令为:
这样就创建了一个名为 test 的数据库,接着我们使用 psql 连接到数据库:
1 2 pgsql/bin/psql test select pg_backend_pid();
如下图,我们使用 psql 连接到数据库后,调用 pg 的 select pg_backend_pid(); 函数查询到当前连接的 pg 后端程序的进程号:
然后我们可以通过 attach 进程的方法进行调试:
参考资料
https://wiki.postgresql.org/wiki/So,_you_want_to_be_a_developer%3F http://www.postgres.cn/v2/news/viewone/1/459 https://blog.frognew.com/2023/11/install-postgresql-16-from-source-code.html https://github.com/microsoft/WSL/issues/8516