CMU 15-721 Project 1 - Foreign Data Wrapper
Pre
2003 年,SQL 标准中增加了一个访问远程数据的规范,称为外部数据的 SQL 管理(SQL/MED)。从 9.1 版开始,PostgreSQL 就开始开发这个特性来实现 SQL/MED 的一部分。在 SQL/MED 中,远程服务器上的表称为外部表。PostgreSQ L的外部数据包裹器(FDW)使用 SQL/MED 来管理类似于本地表的外部表。
Use
使用 FDW 的核心就在于使用外部表(FOREIGN TABLE)。根据SQL/MED标准需要在PG端依次创建以下几个数据库对象:
- 使用
CREATE EXTENTSION
向PG安装某个数据源的FDW扩展。 - 使用
CREATE FOREIGN DATA WRAPPER
语句创建该数据源的FDW对象。 - 使用
CREATE SERVER
语句创建该数据源的服务器对象。 - 使用
CREATE USER MAPPING
语句创建外部数据源用户与PG用户的映射关系(这一步是可选的,比如外部数据源根本没有权限控制时,也就无需创建 USER MAPPING 了)。 - 使用
CREATE FOREIGN TABLE
语句创建外部表。 - 使用
SELECT
语句按照访问普通表的方式访问外部表,如果该数据源支持写操作且它的FDW也已实现支持写操作的相关接口,则也可以使用INSERT
,UPDATE
或DELETE
语句去更新外部表。
FDW 创建语法
--0
create extension postgres_fdw;
grant usage on foreign data wrapper postgres_fdw to u1;
---1
create foreign data wrapper postgres_fdw;
drop foreign data wrapper postgres_fdw;
---2
CREATE SERVER foreign_server
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (host '8.8.8.8', port '1234', dbname 'testdb');
---3
CREATE USER MAPPING FOR public
SERVER foreign_server
OPTIONS (user 'dbuser', password '123456');
---4
create foreign table foreign_table(col1 int,col2 int)
server foreign_server options(schema_name 'public', table_name 'test_table');
---5
select * from foreign_table;
---6 创建文件外部数据包裹器
CREATE EXTENSION IF NOT EXISTS file_fdw;
CREATE SERVER IF NOT EXISTS file_fdw_server FOREIGN DATA WRAPPER file_fdw;
CREATE FOREIGN TABLE IF NOT EXISTS file_table
(
c1 varchar,
c2 int,
c3 int
) SERVER file_fdw_server OPTIONS
(
filename '/file',
tablename 'file_tale'
);
FDW 所用的数据结构
使用 DDL 语法定义数据对象。
1. FOREIGN DATA WRAPPER 对象
该对象的实质是向PG注册了某个数据源的FDW所实现的两个自定义函数——该FDW所实现的所有接口的注册函数(在CREATE FOREIGN DATA WRAPPER
语句中称为HANDLER)以及该FDW的所支持的选项验证函数(在CREATE FOREIGN DATA WRAPPER
语句中称为VALIDATOR)。 系统表 pg_proc(保存所有自定义函数的元数据) 中,且两者的 OID 以及该 FOREIGN DATA WRAPPER 对象的名称与 OID 一同被保存至系统表 pg_foreign_data_wrapper 中。HANDLER 的作用是将该 fdw 实现的一系列 fdw 回调函数的地址打包返回给 PG,从而使 PG 之后访问外部表时可以调用这些访问外部数据的函数。而所谓的 fdw 回调函数则是指 PG 手册所提及的下述接口的实现。
2. FOREIGN SERVER 对象
表示的是外部数据源的数据库对象(比如可以在 CREATE SERVER 时通过选项指定数据库所在服务器的 IP 地址等信息)。FOREIGN SERVER 对象被创建后,相关的元数据被保存在系统表 pg_foreign_server。
3. FOREIGN TABLE对象
将外部数据源的数据组织为表的形式,这样的表就被称作为外部表。当外部表对象被创建后,它与 PG 中的普通表一样,元数据都会被保存在系统表 pg_class 中,只是它的relkind字段会f进行标识。同时,该表在也会在系统表 pg_foreign_table 被保存一条记录,它存储了该表在 pg_class 的 OID 与该表所属的 FOREIGN SERVER 的 OID 的对应关系。
FDW 所使用的回调函数
一个 FDW 的实现的核心就是实现一组回调函数,从而在查询外部表对象的 SQL 的执行过程中可以将运行逻辑切换至自定义的扩展代码中,进而遵照PG的内部机制实现对外部数据源的访问。
这些回调函数都是由 PG 的 Optimizer 和 Executor 进行调用。
Procedure
Source Code
对于一个外部数据源而言,只需要实现了上述的7个回调函数,就可以支持 PG 对于该外部数据源以通用的 SQL 语句实现简单的查询功能。当然,如果要真的尝试去实现这些回调函数,还是需要通过PG的一些专门面向 FDW 提供的接口来与 PG 进行交互。
(定义在 foreign.c 文件中,该文件还定义了所需的结构体)。
这个结构体描述了外部数据包裹器
foreign.c文件中,提供了与FdwRoutine数据结构相关接口的实现.主要功能是将内核里各个数据对象分别放到ForeignDataWrapper, ForeignServer, UserMapping, ForeignTable,FdwRoutine这些结构体的对象中,也就是给这些结构体的对象成员赋值.
foreign.h文件中,提供了用于描述包装器的数据结构, 包括ForeignDataWrapper, ForeignServer, UserMapping, ForeignTable,以及获得这些对象的方法.这些结构里主要存储了该对象在postgres中的oid,以及描述这些结构名字的字符信息.
3.内核提供的创建外部数据源计划节点和数据类型管理的接口
1.提供一个生成路径的方法,用于为外部表生成一个连接路径.(路径是指对于一组基本表进行连接操作的顺序).
2.提供从计划到SQL的接口,生成的SQL会在BeginForeignScan方法中被使用.
3.提供构造访问外部数据源的计划节点接口nodeForeignScan.
fdw用户所写代码得到一些信息,内核提供构造一个用于访问外部数据源的node的方法,用这些信息生成一个node,并返回给查询优化器模块.
4.提供从执行器得到tuple,存储为本地tuple的接口.
把nodeForeignScan扫描出来的tuple,存储为postgres所支持的存储方式.
5.内核提供管理网络和内存的函数,用户处理中间数据的存储,和网络连接的管理.
Postgres用于内存管理的palloc0函数.
4.内核中生成执行计划和用执行器执行相关接口
1.与scan相关接口函数,均在用于访问外部数据的执行器代码nodeforeignscan.c中被调用,如图所示:
2与计划有关的接口函数,会在优化器相关的文件中被调用allpaths.c和createplan.c,如图所示:
执行1处的SQL语句,本质是向postgres中定义一个用C语言编写的udf函数,这个函数就是用户编写的postgres_fdw.c中的必须要实现的postgres_fdw_handler函数,在这个函数中用户把自己实现的7个基本函数注册到fdwroutine结构体对象的各个字段中.这样内核中就保存了这7个函数.