Bitwarden 本地开发库 PostgreSQL 查询指南:只读连接、psql 实战与 EF 方言迁移要点

发布时间:2026/9/13 10:56:28
Bitwarden 本地开发库 PostgreSQL 查询指南:只读连接、psql 实战与 EF 方言迁移要点 Bitwarden 本地开发库 PostgreSQL 查询指南只读连接、psql 实战与 EF 方言迁移要点【免费下载链接】serverBitwarden infrastructure/backend (API, database, Docker, etc).项目地址: https://gitcode.com/GitHub_Trending/ser/server本指南围绕 Bitwarden 服务器仓库server本地开发环境中的 PostgreSQL 数据源系统讲解如何安全、正确地连接vault_dev开发库并执行只读业务查询从环境变量前置检查、只读防护会话建立到宿主psql与容器两种连接方式再到从 MSSQL 参考示例迁移到 PostgreSQL 方言时的关键差异与高频错误规避。读完本文你将能在 Bitwarden 本地开发库上直接回答有多少组织/成员、某个用户的归档状态等业务问题且不会误写任何数据。本地开发环境中的 PostgreSQL 实例Bitwarden server 仓库使用 Docker Compose 拉起本地开发数据库PostgreSQL 是三个开发数据源之一另外两个是 MSSQL 与 MySQL。在 dev/docker-compose.yml 中postgres服务的关键配置如下配置项值说明镜像postgres:14本地开发使用 PostgreSQL 14容器名bitwardenserver-postgres-1Compose 自动生成的默认命名宿主端口5432映射到容器内 5432数据库vault_dev开发数据库名由POSTGRES_DB指定初始用户postgres由POSTGRES_USER指定密码来自${POSTGRES_PASSWORD}数据卷postgres_dev_data持久化数据另有.data/postgres/config与.data/postgres/log挂载profilespostgres、ef通过 profile 控制启动ef表示 Entity Framework 数据源组这意味着你可以在宿主机的5432端口直接访问该数据库也可以通过容器名bitwardenserver-postgres-1在容器网络内访问。仓库对 PostgreSQL 的支持还体现在 Entity Framework Core 层面src/Infrastructure.EntityFramework/Infrastructure.EntityFramework.csproj 引用了Npgsql.EntityFrameworkCore.PostgreSQL确认 PostgreSQL 是 EF provider 的一等公民——这一点直接影响了后文要讲的方言差异EF 生成的 schema 不带存储过程。连接前必查四个环境变量在任何连接动作之前先确认以下四个环境变量齐全。注意一个关键事实BW_POSTGRES_USERNAME指向的是只读登录账号bitwarden_data_reader而不是postgres超级用户。也就是说即使拿到了环境变量也不代表你有写权限——这是刻意设计的防护边界。BW_POSTGRES_SERVERPostgreSQL 服务器地址本地开发通常是localhostBW_POSTGRES_DB_NAME数据库名本地开发为vault_devBW_POSTGRES_USERNAME只读登录名bitwarden_data_readerBW_POSTGRES_PASSWORD该只读登录的密码可以用下面的命令一次性校验四个变量是否都已设置[ -n ${BW_POSTGRES_SERVER:-} ] [ -n ${BW_POSTGRES_DB_NAME:-} ] \ [ -n ${BW_POSTGRES_USERNAME:-} ] [ -n ${BW_POSTGRES_PASSWORD:-} ] \ echo Bitwarden PostgreSQL env vars OK || echo MISSING Bitwarden PostgreSQL env var如果输出MISSING Bitwarden PostgreSQL env var应先补齐环境例如通过dev/setup_secrets.ps1与dev/secrets.json.example对应的本地密钥体系再继续否则连接必然失败。只读防护每个会话都必须执行整个探索流程遵守纵深防御defense in depth原则这在 .claude/skills/exploring-bitwarden-data/SKILL.md 中有明确约定第一层数据库登录本身是只读的——bitwarden_data_reader仅有SELECT权限任何写操作在服务器端就会被拒绝与你发送什么 SQL 无关第二层每次会话显式执行SET default_transaction_read_only on;把整个会话事务默认置为只读作为保险。文档中已注明该机制经过验证开启后写入会报错cannot execute INSERT in a read-only transaction。需要特别提醒的是SET语句的作用域是当前会话只对真正执行了该语句的连接生效。因此每个连接、每次会话都要带上它不能依赖上一次会话的遗留状态也不能指望容器内其他连接替你做这件事。SELECT、WITHCTE以及INFORMATION_SCHEMA之类的 introspection 查询均在允许范围内无需额外授权。建立连接宿主 psql 优先容器兜底连接有两条路径按优先顺序排列宿主已安装psql客户端时用宿主机直连未安装时退回容器内部执行。两条路径都以BW_POSTGRES_USERNAME身份连接并保持SET只读防护。方式一宿主psql推荐已安装时PGPASSWORD$BW_POSTGRES_PASSWORD psql \ -h $BW_POSTGRES_SERVER -U $BW_POSTGRES_USERNAME -d $BW_POSTGRES_DB_NAME -At \ -c SET default_transaction_read_only on; \ -c SELECT COUNT(*) FROM \Organization\参数说明参数作用PGPASSWORD...以环境变量方式传密码避免在命令行明文暴露与 SQL Server 侧SQLCMDPASSWORD、MySQL 侧MYSQL_PWD同理见 providers/mysql.md-h/-U/-d服务器、用户名、数据库名均取自环境变量-A非对齐输出适合脚本/程序消费-t仅打印行去掉表头与页脚-c逐条执行语句先执行SET再执行业务查询方式二容器兜底宿主无psql时docker exec -i bitwardenserver-postgres-1 \ psql -U $BW_POSTGRES_USERNAME -d $BW_POSTGRES_DB_NAME -At \ -c SET default_transaction_read_only on; \ -c SELECT COUNT(*) FROM \Organization\容器方式通过受信任的本地 socket 连接因此不需要-h。若 socket 连接被拒绝可仿照 providers/mysql.md 中%reader 匹配 socket的处理思路补-h 127.0.0.1走 TCP。多语句 / CTEheredoc 形式与-i陷阱需要跑多条语句例如 CTE 多表 JOIN时用 heredoc 把整段 SQL 喂给psql。必须加上docker exec -i——没有-istdin 不会被附加到容器输出会静默为空这是最常见的排查盲点。docker exec -i bitwardenserver-postgres-1 psql -U $BW_POSTGRES_USERNAME -d $BW_POSTGRES_DB_NAME -At PSQL SET default_transaction_read_only on; SELECT o.Name, COUNT(ou.Id) AS members FROM Organization o LEFT JOIN OrganizationUser ou ON ou.OrganizationId o.Id AND ou.Status 2 GROUP BY o.Name ORDER BY members DESC LIMIT 10; PSQL这里ou.Status 2的语义可以在 src/Core/AdminConsole/Enums/OrganizationUserStatusType.cs 中得到确认OrganizationUserStatusType枚举中Confirmed 2表示管理员已授予访问权限、完成密钥交换的正式成员即成员统计的活跃口径。另外注意 heredoc 标签要加引号PSQL。不加引号时 bash 会先对 SQL 内的$做变量展开破坏列引用——这是 SKILL.md 中列出的跨 provider 公共陷阱。方言迁移要点从 MSSQL 参考示例到 PostgreSQLBitwarden 的 schema 文档与示例大量以 MSSQLsqlcmd为参考迁移到 PostgreSQL 时有五个关键差异其中第一个是最高频的错误来源1. 所有 PascalCase 标识符必须加双引号#1 错误来源EF 生成的列名区分大小写例如OrganizationUser、Status、Id、Name。PostgreSQL 对未加引号的标识符会先折叠成小写再查找于是Organization会被解释为organization直接报relation does not exist。因此每一条查询都要给每个 PascalCase 表名、列名加上双引号SELECT COUNT(*) FROM Organization; -- 正确 SELECT COUNT(*) FROM Organization; -- 错误折叠为 organization不存在这是从 MSSQL 参考迁移时最容易踩的坑务必逐列检查。2. 语法家族差异MSSQL 写法PostgreSQL 写法[brackets]无方括号改用双引号包裹标识符TOP nLIMIT n放在ORDER BY之后GETUTCDATE()now() AT TIME ZONE utcWITH (NOLOCK)无此提示直接省略3. 字符串拼接用||MSSQL 的在 PostgreSQL 中无效拼接统一用||。4. 每用户 JSON 列text::jsonb - 键Cipher表上的Archives、Favorites、Folders三列在 MSSQL 侧是NVARCHAR(MAX)见 src/Sql/dbo/Vault/Tables/Cipher.sql 中Favorites、Folders、Archives的定义在 PostgreSQL 中对应text类型内容是按用户 GUID 为键的 JSON 对象。取某个用户的归档/收藏/文件夹状态时先用::jsonb把列转成 JSONB 再按键取值Archives::jsonb - UPPERCASE-GUID键必须是大写的 GUID 字符串。这里有一个与业务语义强相关的细节同样记录在 SKILL.md 的 grounding rules 中归档状态真正存储在Archives这个每用户 JSON 里而Cipher表上虽然也有ArchivedDate列但归档流程从未写入它——查询ArchivedDate会永远返回空却看起来很合理。因此统计归档时要查ArchivesJSON而不是ArchivedDate。5. MSSQL 规范 TVF 不存在从函数源码重建逻辑MSSQL 侧的规范访问控制函数UserCipherDetails、UserCollectionDetails表值函数封装了成员状态、组织启用、直接授权优先于组授权等规则在 PostgreSQL 中不存在——EF provider 不生成任何存储过程或函数。需要自行重建其逻辑时应以上述函数源码为准它们在 .claude/skills/exploring-bitwarden-data/references/sources.md 中有完整映射src/Sql/dbo/Vault/Functions/UserCipherDetails.sql—— 用户 X 能看到哪些 ciphersrc/Sql/dbo/Vault/Functions/CipherDetails.sql—— 按用户投影的 cipher 详情含按用户计算的 ArchivedDatesrc/Sql/dbo/AdminConsole/Functions/UserCollectionDetails.sql—— 用户 X 能访问哪些集合及其有效权限src/Sql/dbo/AdminConsole/Views/OrganizationAbilityView.sql—— 组织功能开关汇总仓库提示SKILL.md也明确优先复用这些规范函数而非手写 JOIN因为手写很容易在成员状态、组织启用、直接/组授权优先级上重建出错。同时references/schema-discovery-queries.md 提供了活库 introspection 的查询集列表、列描述、外键、函数/视图定义可用于核实 schema 现状。实战示例组织成员排行含表结构依据把上述要点串起来一个完整的按成员数排序的组织 Top 10查询如下已在文档中验证可用docker exec -i bitwardenserver-postgres-1 psql -U $BW_POSTGRES_USERNAME -d $BW_POSTGRES_DB_NAME -At PSQL SET default_transaction_read_only on; SELECT o.Name, COUNT(ou.Id) AS members FROM Organization o LEFT JOIN OrganizationUser ou ON ou.OrganizationId o.Id AND ou.Status 2 GROUP BY o.Name ORDER BY members DESC LIMIT 10; PSQL表结构依据src/Sql/dbo/Tables/Organization.sql 定义了Organization表的Id、Name、Seats、Enabled、Status、PlanType等列其中Enabled 1才是组织启用标志Status是 provider 管理生命周期Plan是展示字符串聚合/过滤应使用PlanType成员口径依据OrganizationUser.Status 2对应Confirmed见 OrganizationUserStatusType.cs即活跃成员标准口径。如果问题语义不同例如按席位占用口径Status IN (0,1,2)统计需要在查询中显式声明口径因为活跃成员在 schema 层面是有歧义的——这是 skill 的 eval 记录中反复出现的高频误判点。与 MSSQL、MySQL 参考的横向对照三个 provider 的完整参考文档分别位于 providers/mssql.md、providers/mysql.md 与本文对应的 providers/postgresql.md。核心差异速查维度MSSQLMySQLPostgreSQL环境变量前缀BW_MSSQL_*BW_MYSQL_*BW_POSTGRES_*客户端sqlcmdmysqlpsql只读会话防护-K ReadOnly驱动层SET SESSION TRANSACTION READ ONLY;SET default_transaction_read_only on;标识符引用[brackets]反引号仅保留字如Group必须双引号所有 PascalCase 都必须行数限制TOP nLIMIT nLIMIT nUTC 时间GETUTCDATE()UTC_TIMESTAMP()now() AT TIME ZONE utc每用户 JSON 列NVARCHAR(MAX)longtexttext用::jsonb - 键取值语法无JSON_MODIFY 写JSON_UNQUOTE(JSON_EXTRACT(col, $.KEY))col::jsonb - KEY规范 TVF存在UserCipherDetails等不存在不存在从 sources.md 重建使用建议与安全底线密码永不外泄密码一律内联在命令中传递如PGPASSWORD...不要export、不要printenv、不要cat、不要 hexdump——这是 SKILL.md 的硬性约定结果展示少于 20 行输出为 Markdown 表格大结果集用 top-N 计数概括汇报时回显所执行的 SQL并去掉psql的行数页脚写操作边界只读登录在服务器端兜底SET default_transaction_read_only on;在会话端兜底两条防线缺一不可——本技能不用于任何写库、迁移或种子修改场景schema 真相在仓库不要在脑中臆测 vault schema 的样子优先查阅 src/Sql/dbo/ 的 SSDT schema、sources.md 的源码映射或对活库执行 schema-discovery-queries.md 中的 introspection 查询。按本文的流程操作你可以在 Bitwarden 本地 PostgreSQL 开发库上安全、只读地完成组织/成员统计、归档状态核查、集合权限检查等绝大多数探索性业务查询并把从 MSSQL 参考迁移时的方言成本降到最低。【免费下载链接】serverBitwarden infrastructure/backend (API, database, Docker, etc).项目地址: https://gitcode.com/GitHub_Trending/ser/server创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考