FDW (foreign-data wrapper,外部数据包装器),PostgreSQL FDW 是一种外部访问接口,它可以被用来访问存储在外部的数据,这些数据可以是外部的pg数据库,也可以oracle、mysql等数据库,甚至可以是文件。
可以让我们在PostgreSQL 中使用SQL查询极为丰富的外部数据:
主流关系型数据库:Oracle、MySQL、SQL Server等
NoSQL数据库:ClickHouse、MongoDB、Redis、Neo4j等
外部文件:csv、josn、pg_dump、xml
Web文件:S3、Twitter、Facebook…
fdw外部数据源
简单来说,通过在PG库里建立其他数据库的外部表或者视图的方式,访问其他数据库表数据的方式就是FDW。
处理外部数据源的插件(每类数据库各有不同,需要分别安装):
--创建extension
create extension postgres_fdw;
--查询extension对应视图
select * from pg_foreign_data_wrapper;
--查看extension插件
select * from pg_extension;
--删除extension
drop extension postgres_fdw;
连接目标库(要访问的IP、端口、DB名)
--创建server
CREATE SERVER foreign_server
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (host 'IP', port '端口', dbname 'DB名');
--查询对应server视图
select * from pg_foreign_server;
或者
\des
--删除server
drop SERVER foreign_server;
用户映射/账号映射,目标库使用的账号/密码(可以单独创建指定专用用户,也可以用现有用户);
--创建User Mapping
CREATE USER MAPPING FOR postgres
SERVER foreign_server
OPTIONS (user '账号', password '密码');
--查看User Mapping视图
select * from pg_user_mappings;
--删除User Mapping
DROP USER MAPPING for user_name SERVER foreign_server;
本地外部表对应目标库哪张表或视图,外部表字段可以少于目标表和视图,按需索取;
--创建外部表、视图
CREATE FOREIGN TABLE fdw_foreign_table(
id integer NOT NULL
)
SERVER foreign_server
OPTIONS (
schema_name 'public',
table_name 'foreign_table'
);
-- 查询外部表对应视图
select * from pg_foreign_table;
--删除外部表、视图
drop FOREIGN TABLE fdw_foreign_table;
pg .11开始,可以用下面语句导入表定义
--导入全表
IMPORT FOREIGN SCHEMA foreign_films
FROM SERVER film_server INTO films;
--只导入部分字段
IMPORT FOREIGN SCHEMA foreign_films LIMIT TO (id, name)
FROM SERVER film_server INTO films;
pg .14开始,如果外部用户有权限,现在可以对外部表执行INSERT、UPDATE、DELETE、COPY、TRUNCATE
操作。对于INSERT
,目前不支持ON CONFLICT DO UPDATE
子句,但支持了CONFLICT DO NOTHING
子句。
--查询外部表结构
\d fdw_foreign_table
--查询外部表数据
select * from fdw_foreign_table;
--返回postgres_fdw建立的外部连接及状态
SELECT * FROM postgres_fdw_get_connections() ORDER BY 1;
--断开指定连接
SELECT postgres_fdw_disconnect('xxx');
--断开所有连接
SELECT postgres_fdw_disconnect_all();
create extension mysql_fdw;
CREATE SERVER mysql_server
FOREIGN DATA WRAPPER mysql_fdw
OPTIONS (host 'ip', port '端口', dbname 'DB名');
CREATE USER MAPPING FOR pg_rw
SERVER mysql_server
OPTIONS (user '账号', password '密码');
CREATE FOREIGN TABLE fdw_mysql_mytab (
id int,
name character varying(128)
)
SERVER mysql_server
OPTIONS (
dbname 'DB名',
table_name 'table名'
);
将csv格式文件创建为外部表,file_fdw不需要用户映射。
create extension file_fdw;
CREATE SERVER log_server FOREIGN DATA WRAPPER file_fdw;
CREATE FOREIGN TABLE postgres_log
(
log_time timestamp,
user_name text,
database_name text,
process_id integer,
connection_from text,
session_id text,
session_line_num bigint
) SERVER log_server
OPTIONS (format 'csv', header 'false', filename '/data/postgresql-01.csv', delimiter ',', null'');
实现一个FDW的核心是实现一组回调函数,有了这些回调函数的帮助, 在查询外部表对象的执行过程中就可以将运行逻辑切换至自定义的扩展代码中, 进而遵照PG的内部机制实现对外部数据源的访问。
目前PostgreSQL11 beta2,提供的FDW回调函数接口有39个。FDW的实现者需要根据外部数据源自身的能力(比如是否支持写操作,以及是否支持在外部数据源端执行join操作等等)对这些接口有选择地予以实现。
这些接口中, 最核心的接口有7个。无论外部数据源自身能力如何, 这7个接口是实现通过外部表对象访问该数据源的必须接口。它们的接口定义如下:
typedef void (*GetForeignRelSize_function) (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid);
typedef void (*GetForeignPaths_function) (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid);
typedef ForeignScan *(*GetForeignPlan_function) (PlannerInfo *root, RelOptInfo *baserel,Oid foreigntableid, ForeignPath *best_path, List *tlist, List *scan_clauses, Plan *outer_plan);
typedef void (*BeginForeignScan_function) (ForeignScanState *node, int eflags);
typedef TupleTableSlot *(*IterateForeignScan_function) (ForeignScanState *node);
typedef void (*ReScanForeignScan_function) (ForeignScanState *node);
typedef void (*EndForeignScan_function) (ForeignScanState *node);
在PG中,查询语句经过以下5个子系统处理:
可以整合成三个大阶段:
PG的FDW所需的7个回调函数主要是在Optimizer和Executor阶段进行“介入”:
GetForeignRelSize:
void GetForeignRelSize (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid);
GetForeignPaths
void GetForeignPaths (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid);
GetForeignPlan
ForeignScan * GetForeignPlan (PlannerInfo *root, RelOptInfo *baserel, Oid foreigntableid, ForeignPath *best_path, List *tlist, List *scan_clauses, Plan *outer_plan);
BeginForeignScan
void BeginForeignScan (ForeignScanState *node, int eflags);
IterateForeignScan
TupleTableSlot * IterateForeignScan (ForeignScanState *node);
ReScanForeignScan
void ReScanForeignScan (ForeignScanState *node);
EndForeignScan
void EndForeignScan (ForeignScanState *node);