最近突然遇到了一批使用fdw的场景,整理记录一把。
FDW (foreign-data wrapper,外部数据包装器),可以让我们在PG中使用SQL查询极为丰富的外部数据:
下面的例子希望从本实例的postgres库访问clair库的layer表
首先在目标库创建一个用户,并授权它可以查询layer表
psql -dclair
create user clair_r with password '123456';
grant select on public.layer to clair_r;
按照四部曲:
处理外部数据源的插件,每类数据库各有不同,需要分别安装。
创建语句
create extension postgres_fdw;
clair=# create extension postgres_fdw;
ERROR: could not open extension control file "/…/base/share/extension/postgres_fdw.control": No such file or directory
解决方法
对应视图
select * from pg_foreign_data_wrapper;
查看插件
select * from pg_extension ;
或
\dx
删除语句
drop extension postgres_fdw;
目标库连接串,要访问哪个ip、端口、db名
创建语句
CREATE SERVER clair_server
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (host '192.83.123.89', port '5432', dbname 'clair');
对应视图
select * from pg_foreign_server;
或
\des
删除语句
drop SERVER foreign_server;
用户映射:目标库使用哪个用户、密码,可以单独创建一个用户也可以用现有的
CREATE USER MAPPING FOR postgres
SERVER clair_server
OPTIONS (user 'clair_r', password '123456');
对应视图
select * from pg_user_mappings;
或
\deu+
删除语句
DROP USER MAPPING for user_name SERVER server_name;
本地外部表对应目标库哪张表或视图,外部表字段可以少于目标表和视图,只取自己需要的
CREATE FOREIGN TABLE fdw_layer(
id integer NOT NULL,
name character varying(128)
)
SERVER clair_server
OPTIONS (schema_name 'public', table_name 'layer');
对应视图
select * from pg_foreign_table;
删除语句
drop FOREIGN TABLE foreign_table;
从pg 11开始,也可以用下面语句导入表定义
IMPORT FOREIGN SCHEMA foreign_films
FROM SERVER film_server INTO films;
也可以只导入部分字段
IMPORT FOREIGN SCHEMA foreign_films LIMIT TO (actors, directors)
FROM SERVER film_server INTO films;
在pg 14中,如果外部用户有权限,现在可以对外部表执行INSERT
, UPDATE
, DELETE
, COPY
以及 TRUNCATE
操作。对于
insert
,目前不支持
ON CONFLICT DO UPDATE子句,但支持了CONFLICT DO NOTHING子句。
\d fdw_layer
select * from fdw_layer;
SELECT * FROM postgres_fdw_get_connections() ORDER BY 1;
SELECT postgres_fdw_disconnect('loopback1');
SELECT postgres_fdw_disconnect_all();
按照四部曲
create extension mysql_fdw;
CREATE SERVER mysql_server
FOREIGN DATA WRAPPER mysql_fdw
OPTIONS (host '192.83.123.89', port '3306', dbname 'mydb');
CREATE USER MAPPING FOR pg_rw
SERVER mysql_server
OPTIONS (user 'mydb_r', password '123456');
CREATE FOREIGN TABLE fdw_mysql_mytab (
id int,
name character varying(128)
)
SERVER mysql_server
OPTIONS (
dbname 'mydb',
table_name 'mytab'
);
查询外部表
psql -Upg_rw
select count(*) from fdw_mysql_mytab;
将csv格式的pg错误日志创建为外部表。file_fdw不需要用户映射,因此只有3步
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'');
以下内容来自:https://oyo-byte.github.io/2018/08/03/learn_about_pgfdw/
实现FDW的核心是实现一组回调函数,其中最核心有7个。无论外部数据源自身能力如何, 这7个接口是实现通过外部表对象访问该数据源的必须接口,定义都位于fdwapi.h。
回调函数 |
PG中的调用时机 |
作用 |
GetForeignRelSize |
优化器生成访问路径的过程中对外部表估算访问代价时 |
提供外部表对于计算访问代价所需的基础数据,如表的元组数以及元组的平均长度,并将这些数据保存在输入参数baserel的字段”rows”以及”width”中 |
GetForeignPaths |
生成对外部表访问路径时 |
生成对目标外部表的访问路径(通过PG中的接口createforeignscanpath()生成) |
GetForeignPlan |
优化器生成扫描外部表的查询计划节点时 |
生成访问目标外部表的ForeignScan计划节点(通过PG中的接口make_foreignscan()) |
BeginForeignScan |
执行器即将开始执行ForeignScan算子,进行该算子相关的初始化时 |
获取执行ForeignScan算子所需的信息,并将它们组织并保存在ForeignScanState中 |
IterateForeignScan |
执行ForeignScan算子过程中需要获取下一元组时 |
读取外部数据源的一行数据,并将它组织为PG中的Tuple(即TupleTableSlot). 当该回调函数返回一个空的TupleTableSlot结构时, 迭代器停止迭代 |
ReScanForeignScan |
执行Nested Loop过程中需要重置Inner Scan时(即Outter Scan需要向前推进一行时) |
将外部数据源的读取位置重置回最初的起始位置 |
EndForeignScan |
ForeignScan算子执行完成时 |
释放整个ForeignScan算子执行过程中占用的外部资源或FDW中的资源 |
参考
PostgreSQL Insider - How to link to Oracle databases using oracle_fdw (part 1)
http://v0.pigsty.cc/pdf/fdw-pgconf-2019.pdf
学习PostgreSQL的FDW(#1) | oYo-Byte
https://oyo-byte.github.io/2018/10/07/how_to_write_a_pg_fdw/
PostgreSQL: Documentation: 14: F.35. postgres_fdw
https://www.postgresql.org/docs/14/sql-importforeignschema.html
PostgreSQL: Documentation: 14: F.14. file_fdw