NocoBase 连接外部数据表(FDW):基于 MySQL federated 与 PostgreSQL Foreign Data Wrapper 的远程表接入实战
【免费下载链接】nocobaseNocoBase is an open-source AI + no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase
导读
本指南讲解 NocoBase 中「连接外部数据表(FDW)」功能:它基于数据库原生的 Foreign Data Wrapper 技术,把远程 MySQL、MariaDB、PostgreSQL 中的数据表映射为本地数据表使用,适用于跨数据源集成、只读数据共享、遗留系统对接等场景。读完本文,你将掌握 FDW 与「连接数据源」的区别、MySQLfederated引擎与 PostgreSQLpostgres_fdw/mysql_fdw的启用前提、插件安装激活流程,以及「创建数据库服务 → 选择远程表 → 字段同步」的完整操作步骤,并深入理解底层 plugin-collection-fdw 插件的实现原理。
什么是 FDW:连接数据源 vs 连接外部数据表
NocoBase 官方文档对两者做了明确的区分:
- 连接数据源:与特定数据库或 API 服务建立连接,可以完整使用数据库的特性或 API 提供的服务,数据源作为一个整体被接入。
- 连接外部数据表:从外部获取数据并映射到本地使用。在数据库领域,这种技术被称为 FDW(Foreign Data Wrapper,外部数据包装器)。它侧重于将远程表当作本地表使用,只能一张一张表地连接。因为是远程访问,使用时会有各种约束和局限。
二者也可以配合使用:前者用于建立与数据源的连接,后者用于跨数据源访问。例如,连接了某个 PostgreSQL 数据源,而这个数据源里恰好有一个表是基于 FDW 创建的外部数据表。
从仓库实现看,该能力由独立插件 plugin-collection-fdw 提供。在 server/plugin.ts 的beforeEnable钩子中,插件会校验主数据库类型:
async beforeEnable() { if (this.db.inDialect('sqlite')) { throw new Error('sqlite does not support foreign data wrapper'); } }也就是说,SQLite 作为 NocoBase 主数据库时无法启用该插件,FDW 仅支持 MySQL、MariaDB、PostgreSQL 作为主数据库的场景。
支持的数据库组合
MySQL / MariaDB:federated引擎
MySQL 通过federated引擎实现外部表,需要手动激活(默认未开启),支持连接远程 MySQL 及其协议兼容数据库(如 MariaDB)。
PostgreSQL:多种fdw扩展
在 PostgreSQL 中,可通过不同类型的fdw扩展来支持不同的远程数据类型。目前 NocoBase 支持的扩展有:
| 扩展 | 用途 | 说明 |
|---|---|---|
postgres_fdw | 在 PostgreSQL 中连接远程 PostgreSQL 数据库 | PostgreSQL 官方内置扩展 |
mysql_fdw | 在 PostgreSQL 中连接远程 MySQL 数据库 | 由 EnterpriseDB 维护的开源扩展 |
其余类型的 fdw 扩展(如file_fdw、oracle_fdw等)接入 NocoBase 需要在代码中实现相应的适配接口。
从源码结构看,插件通过「Bridge(桥接)」抽象来支撑不同数据库组合。在 server/plugin.ts 的beforeLoad中注册了三组桥接实现:
RemoteLocalBridgeFactory.registerBridge('postgres', 'postgres', PgToPgBridge); RemoteLocalBridgeFactory.registerBridge('mariadb', 'mariadb', MariadbToMariadbBridge); RemoteLocalBridgeFactory.registerBridge('mysql', 'mysql', MySQLToMySQLBridge);对应的实现文件位于 bridges 目录:
- pg-to-pg.ts:PostgreSQL → PostgreSQL,基于
postgres_fdw; - mysql-to-mysql.ts:MySQL → MySQL,基于
federated引擎; - mariadb-to-mariadb.ts:MariaDB → MariaDB;
- mysql-to-pg.ts:PostgreSQL 主库连接远程 MySQL,对应
mysql_fdw; - remote-local-bridge.ts:桥接的抽象基类与工厂。
前提条件
使用 FDW 功能前,需要满足以下条件:
- 如果 NocoBase 的主数据库是 MySQL,则需要激活
federated引擎,具体步骤见 MySQL 如何启用 federated 引擎; - 通过插件管理器安装并激活
collection-fdw插件。
附:MySQL 如何启用 federated 引擎
MySQL 数据库默认没有开启federated模块,需要修改my.cnf配置。如果使用 Docker 部署,可以通过 volumes 挂载扩展配置。以docker-compose.yml为例:
mysql: image: mysql:8.1.0 volumes: - ./storage/mysql-conf:/etc/mysql/conf.d environment: MYSQL_DATABASE: nocobase MYSQL_USER: nocobase MYSQL_PASSWORD: nocobase MYSQL_ROOT_PASSWORD: nocobase restart: always networks: - nocobase新建配置文件./storage/mysql-conf/federated.cnf:
[mysqld] federated重启 MySQL 容器使配置生效:
docker compose up -d mysql最后验证federated是否已经激活:
show engines如果输出结果中FEDERATED一行的 Support 列为YES,则说明引擎已成功启用。
使用手册:连接外部数据表的完整流程
插件激活后,即可在界面上完成外部数据表的连接。整个流程分为六步。
1. 进入「连接外部数据」
在「数据表管理 > 创建数据表」下拉菜单中,选择「连接外部数据」。
2. 选择或创建数据库服务
在「数据库服务」下拉选项中,选择已存在的数据库服务;如果没有,可以点击「创建数据库服务」。
创建数据库服务时需要填写远程数据库的连接信息,包括数据库类型、主机地址(host)、端口(port)、数据库名(database)、用户名(username)和密码(password)等。
3. 选择远程表
选择数据库服务之后,在「远程表」的下拉选项中,选择需要连接的数据表。这一步由前端组件RemoteTableSelect驱动,服务端通过DatabaseServerModel的listRemoteTables()方法实时拉取远程库的表清单(见下文原理章节)。
4. 配置字段信息
选中远程表后,NocoBase 会自动读取远程表的字段结构(列名、类型、是否主键等),你可以在此基础上调整字段映射配置,例如修改字段的 interface 类型、显示名称等。
5. 从远程表同步(结构变更)
如果远程表结构发生变化(新增/删除/修改了列),无需重建连接,直接点击「从远程表同步」即可将最新结构同步到本地。同步对话框会展示结构差异,确认后完成更新。这一能力对应前端组件SyncFieldsAction与服务端的字段描述/同步逻辑。
6. 在界面中使用
完成上述步骤后,外部数据表会像普通数据表一样出现在数据表管理中,可以继续为其创建页面、区块(表格、表单、详情等)和操作按钮,在界面中直接展示和查询远程数据。
说明:上述操作对应的前端交互组件(数据库服务选择、创建/编辑数据库服务、远程表选择、字段预览、字段同步、不支持的字段提示等)均可在 client 组件目录 中找到,包括
CreateDatabaseServerAction.tsx、EditDatabaseServerAction.tsx、DatabaseServerSelect.tsx、RemoteTableSelect.tsx、PreviewFields.tsx、PreviewTable.tsx、SyncFieldsAction.tsx、UnSupportFields.tsx等。
原理剖析一:数据库服务(Database Server)模型与生命周期
FDW 功能的核心数据模型是databaseServers集合,其定义位于 collections/database-server-collection.ts:
export default defineCollection({ name: 'databaseServers', dataCategory: 'system', dumpRules: 'required', migrationRules: ['overwrite', 'schema-only'], autoGenId: false, model: 'DatabaseServerModel', fields: [ { type: 'string', name: 'name', primaryKey: true }, { type: 'string', name: 'description' }, { type: 'json', name: 'options' }, ], });每条数据库服务记录以name为主键,远程连接参数(host、port、database、username、password 等)整体存放在optionsJSON 字段中。实际执行逻辑由自定义模型 DatabaseServerModel 承担,它通过事件钩子与数据库操作绑定,见 server/plugin.ts:
databaseServers.beforeCreate→createServer():校验远程连通性,并通过 Bridge 在本地库创建CREATE SERVER/CREATE USER MAPPING等对象;databaseServers.afterUpdate→updateServer():先authenticate()验证新连接,再执行ALTER SERVER ... OPTIONS (SET ...)更新连接参数;databaseServers.afterDestroy→destroyServer():执行DROP SERVER IF EXISTS ... CASCADE清理本地对象。
此外,插件注册了databaseServers:testConnection资源动作(plugin.ts),用于在创建服务前测试远程连接:
this.app.resourceManager.registerActionHandlers({ async 'databaseServers:testConnection' { const values = ctx.app.environment.renderJsonTemplate(ctx.action.params.values || {}); const db = new Database({ dialect: app.db.options.dialect, ...values }); try { await db.sequelize.authenticate(); } catch (error) { throw new Error(`Unable to connect to the remote database: ${error.message}`); } ctx.body = { success: true }; await next(); }, });值得注意的一个细节:连接参数在写入/读取时会经过app.environment.renderJsonTemplate()渲染(见 database-server.ts),这意味着数据库服务的连接参数支持使用环境变量模板,便于在不同部署环境间复用配置。
原理剖析二:Bridge 机制与底层 SQL
「远程库 → 本地库」的映射由 Bridge 抽象完成。以 PostgreSQL → PostgreSQL 的 pg-to-pg.ts 为例,createServer()生成的底层 SQL 展示了完整的 FDW 建链过程:
CREATE EXTENSION IF NOT EXISTS postgres_fdw; CREATE SERVER IF NOT EXISTS "<serverName>" FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host :host, port :port, dbname :database); CREATE USER MAPPING IF NOT EXISTS FOR "<localDbUsername>" SERVER "<serverName>" OPTIONS (user :user, password :password);createTable()则将远程表的CREATE TABLE定义改写为CREATE FOREIGN TABLE,并附加SERVER ... OPTIONS (schema_name ..., table_name ...)以指向远程对象:
remoteTableDefinition = remoteTableDefinition.replace('CREATE TABLE', 'CREATE FOREIGN TABLE'); remoteTableDefinition = remoteTableDefinition.replace(/\bNOT NULL\b/g, ''); remoteTableDefinition = `${remoteTableDefinition} SERVER "${remoteServerName}" OPTIONS (schema_name '${remoteTableInfo.schema}', table_name '${remoteTableInfo.tableName}')`;两个细节值得注意:
NOT NULL约束被去除:外部表无法可靠保证远程数据的非空约束,因此在本地定义中去掉了NOT NULL;handle_null_id触发器:对于远程表存在id列且主键使用序列(serial)的场景,代码会在远程库上创建一个BEFORE INSERT触发器set_default_id,当插入的id为 NULL 时自动从序列取下一个值(nextval(sequence_name)),从而保证经由本地写入远程数据时主键的完整性。
外部表集合的元数据行为
外部数据表对应的集合类型是 ForeignDataCollection,它重写了若干默认行为:
- 构造函数强制
autoGenId = false、timestamps = false:外部表不自动生成本地自增主键和时间戳字段; onDump()为空实现:外部表不参与数据库备份/恢复的数据导出(结构由远程库负责);onSync()是「同步/重建」的核心:先根据remoteServerName找到数据库服务,重建 server,再通过 Bridge 的createTable()依据远程表的实时定义重建本地 FOREIGN TABLE;removeFromDb()在 PostgreSQL 下执行DROP FOREIGN TABLE IF EXISTS ... [CASCADE]后再移除集合定义;- 通过
field.afterAdd事件,将 interface 为createdAt/updatedAt的字段重新映射为模型的时间戳属性,保证界面上的时间语义正常。
插件的资源权限由 ACL snippetpm.data-source-manager.collection-fdw控制,覆盖databaseServers:*与databaseServers.tables:*两类动作(见 plugin.ts)。
注意事项与适用边界
- 主数据库限制:NocoBase 主库为 SQLite 时无法启用本插件(
sqlite does not support foreign data wrapper); - MySQL 主库需先激活 federated:
federated引擎默认关闭,必须通过my.cnf/ Docker volumes 挂载federated.cnf并重启生效; - 逐表连接:FDW 是一张一张表连接,不是整个数据源接入;每次新增外部表都需要重复「选择数据库服务 → 选择远程表」的流程;
- 远程访问的天然约束:外部表的查询性能受远程网络往返影响,无法使用本地索引优化,
NOT NULL等约束在本地不生效,适合低频查询、数据集成、跨源只读共享等场景; - 结构变更需手动同步:远程表结构变化后,本地外部表不会自动更新,需要在界面中执行「从远程表同步」;
- 环境变量模板:数据库服务的连接参数支持环境变量渲染,生产环境请避免在配置中明文写入敏感信息(如密码),建议通过环境变量注入。
结语
NocoBase 的「连接外部数据表(FDW)」把数据库原生的 Foreign Data Wrapper 能力封装为开箱即用的可视化流程:MySQL 主库通过federated引擎、PostgreSQL 主库通过postgres_fdw/mysql_fdw扩展,均只需在界面上配置数据库服务、选择远程表并同步字段,即可像使用本地表一样使用远程数据。对于需要跨数据源集成、遗留系统对接或数据共享的场景,这是一条无需编写代码即可落地的路径。若需进一步定制(例如接入其他类型的 fdw 扩展),可以从 plugin-collection-fdw 的 Bridge 机制与 bridge 基类 入手扩展实现。
【免费下载链接】nocobaseNocoBase is an open-source AI + no-code platform for building business systems fast. Instead of generating everything from scratch, AI works on top of production-proven infrastructure and a WYSIWYG no-code interface, so you get both speed and reliability.项目地址: https://gitcode.com/GitHub_Trending/no/nocobase
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考