学习PostgreSQL的FDW(#3)-其他函数说明 I

上两篇文章主要介绍了实现FDW的7个必须实现的扫描相关的回调函数,学习PostgreSQL的FDW(#1)
学习PostgreSQL的FDW(#2)-源码跟踪,这边就继续说说其余32个回调函数

用于扫描外部连接的回调函数

如果一个 FDW 支持远程执行外部连接(而不是先把两个表的数据取到本地然后做本地连接),它应该提供这个回调函数:

1
2
3
4
5
6
7
void
GetForeignJoinPaths (PlannerInfo *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
JoinType jointype,
JoinPathExtraData *extra);

它为两个(或更多)同属于一台外部服务器的外部表的连接创建可能的访问路径。这个可选的函数会在查询规划过程中被调用。和GetForeignPaths一样,这个函数应该为提供的joinrel生成ForeignPath路径,并且调用add_path把这些路径加入到该连接应该考虑的路径集合中。但是和GetForeignPaths不一样的是,不需要这个函数产生最少一个路径,因为涉及本地连接的路径总是可用的。

注意的是,对于相同的连接关系,将会重复调用此函数来生成内外关系的不同组合。FDW需要负责最小化其中重复的工作。

如果一个ForeignPath路径被选中用于该连接,它将在整个连接处理中存在,为其中的成分表和子连接产生的路径将不会被使用。后续对该连接路径的处理大部分和扫描单个外部表的路径一样。一点不同是ForeignScan计划节点的scanrelid应该被设置为零,因为它表示的不是单个关系,而是用ForeignScan节点的fs_relids属性来表示被连接的关系集合(fs_relids会被核心规划器代码自动设置,不需要由 FDW 填充)。另一点不同是,由于一个远程连接的列的列表无法在系统目录中找到,FDW必须用一个合适的TargetEntry节点列表来填充fdw_scan_tlist,表示运行时它返回的元组中提供的列的集合。

用于规划扫描/连接 后处理的回调函数

如果一个FDW支持执行远程的扫描/连接 后处理,例如远程聚集,应该事先这个回调函数:

1
2
3
4
5
6
void
GetForeignUpperPaths(PlannerInfo *root,
UpperRelationKind stage,
RelOptInfo *input_rel,
RelOptInfo *output_rel,
void *extra);

上层关系处理创建可能的访问路径,这是规划器针对所有扫描/连接后查询处理的术语,例如聚集、窗口函数、排序和表更新。在查询规划期间会调用这个可选的函数。当前,只有当该查询中涉及的所有基本关系都属于同一个 FDW 时才会调用这个函数。这个函数应该让 FDW 知道如何远程执行的任何扫描/连接 后处理生成ForeignPath路径,并且调用add_path把这些路径加入到上层关系中。就GetForeignJoinPaths来说,并不要求这个函数在创建任何路径时都能成功,因为涉及本地处理的路径总是可行的。

stage参数表示当前正在考虑的是哪一个扫描/连接后处理步骤。output_rel是接收表示这一个步骤的路径的上层关系,而input_rel是表示这个步骤输入的关系。extra参数提供了其他详细信息。目前,它仅能设置成UPPERREL_PARTIAL_GROUP_AGG或UPPERREL_GROUP_AGG,在这种情况下,它指向的是GroupPathExtraData结构体。(注意被加入到output_rel中的ForeignPath路径通常对input_rel的路径没有直接的依赖,因为它们的处理被认为是在外部处理的。不过,检查为前一个处理步骤生成的路径有助于避免冗余的规划工作)。

使用gdb追踪调用情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
(gdb) b postgresGetForeignUpperPaths
Breakpoint 1 at 0x7f2cea563a20: file postgres_fdw.c, line 5437.
(gdb) c
Continuing.

Breakpoint 1, postgresGetForeignUpperPaths (root=0xf7ce50, stage=UPPERREL_GROUP_AGG,
input_rel=0xf7d3d0, output_rel=0x1063910, extra=0x7ffc84ff94e0) at postgres_fdw.c:5437
5437 if (!input_rel->fdw_private ||
(gdb) bt
#0 postgresGetForeignUpperPaths (root=0xf7ce50, stage=UPPERREL_GROUP_AGG, input_rel=0xf7d3d0, output_rel=0x1063910, extra=0x7ffc84ff94e0) at postgres_fdw.c:5437
#1 0x00000000006807bf in create_ordinary_grouping_paths (root=root@entry=0xf7ce50, input_rel=input_rel@entry=0xf7d3d0, grouped_rel=grouped_rel@entry=0x1063910, agg_costs=agg_costs@entry=0x7ffc84ff94b0, gd=gd@entry=0x0, extra=extra@entry=0x7ffc84ff94e0, partially_grouped_rel_p=partially_grouped_rel_p@entry=0x7ffc84ff9488) at planner.c:4059
#2 0x00000000006831e9 in create_grouping_paths (gd=0x0, agg_costs=0x7ffc84ff94b0, target_parallel_safe=true, target=0x1063768, input_rel=0xf7d3d0, root=0xf7ce50) at planner.c:3783
#3 grouping_planner (root=root@entry=0xf7ce50, inheritance_update=inheritance_update@entry=false, tuple_fraction=<optimized out>, tuple_fraction@entry=0) at planner.c:2037
#4 0x0000000000684218 in subquery_planner (glob=glob@entry=0xf7cdc0, parse=parse@entry=0xf7c6a8, parent_root=parent_root@entry=0x0, hasRecursion=hasRecursion@entry=false, tuple_fraction=tuple_fraction@entry=0) at planner.c:966
#5 0x0000000000685236 in standard_planner (parse=0xf7c6a8, cursorOptions=256, boundParams=0x0) at planner.c:405
#6 0x000000000072178c in pg_plan_query (querytree=querytree@entry=0xf7c6a8, cursorOptions=cursorOptions@entry=256, boundParams=boundParams@entry=0x0) at postgres.c:809
#7 0x000000000072186e in pg_plan_queries (querytrees=<optimized out>, cursorOptions=cursorOptions@entry=256, boundParams=boundParams@entry=0x0) at postgres.c:875
#8 0x0000000000721cda in exec_simple_query (query_string=0xf7b810 "select count(*) from t_fdw ;") at postgres.c:1050
#9 0x0000000000722e62 in PostgresMain (argc=<optimized out>, argv=argv@entry=0xfa5528, dbname=0xfa5410 "fdw", username=<optimized out>) at postgres.c:4153
#10 0x000000000047a861 in BackendRun (port=0xf9d3f0) at postmaster.c:4361
#11 BackendStartup (port=0xf9d3f0) at postmaster.c:4033
#12 ServerLoop () at postmaster.c:1706
#13 0x00000000006babb9 in PostmasterMain (argc=argc@entry=3, argv=argv@entry=0xf763c0) at postmaster.c:1379
#14 0x000000000047b2a1 in main (argc=3, argv=0xf763c0) at main.c:228

更新外部表的回调函数

回调函数 作用
AddForeignUpdateTargets 保证FDW能够找到要更新或删除的准确行
PlanForeignModify 执行外部表上插入、更新或删除所需的任何附加规划动作
BeginForeignModify 开始执行一个外部表修改操作,执行任何先于实际表修改的初始化工作
ExecForeignInsert 插入一个元组到外部表
ExecForeignUpdate 更新外部表中的一个元组
ExecForeignDelete 从外部表删除一个元组
EndForeignModify 结束表更新并释放资源
BeginForeignInsert 开始在外表上执行插入操作(分区表或COPY FROM命令),在实际插入之前执行所需的任何初始化
EndForeignInsert 结束表插入并释放资源(分区表或COPY FROM命令)
IsForeignRelUpdatable 报告指定的外部表支持哪些更新操作
PlanDirectModify 决定在远程服务器上执行直接修改是否安全
BeginDirectModify 准备在远程服务器上执行一次直接修改,执行直接修改所需的任何初始化工作
IterateDirectModify 执行直接修改操作
EndDirectModify 在远程服务器上的直接修改后进行清理



如果一个FDW支持可写的外部表,根据FDW的需要和功能,应该提供以下某些或者全部回调函数:

  • AddForeignUpdateTargets
1
2
3
4
void
AddForeignUpdateTargets(Query *parsetree,
RangeTblEntry *target_rte,
Relation target_relation);

UPDATE和DELETE操作是在之前由表扫描函数取出的行上被执行的。FDW可能需要额外的信息(例如一个行ID或主键列的值)来保证它能够找到要更新或删除的准确行。要支持这些要求,这个函数可以向列列表中增加额外的隐藏或“junk”的目标列,它们在一个UPDATE或DELETE期间会被从外部表中获取。

要做到这一点,向parsetree->targetList中增加TargetEntry项,它们包含要被获取的额外值的表达式。每一个这样的项必须被标记为resjunk = true,并且必须有一个可区分的resname用于在执行期间标识它。请避免使用匹配ctidN、wholerow或wholerowN的名字,因为核心系统可能会生成使用这些名字的junk列。如果额外的表达式比简单的Vars更复杂,在将它们添加到目标列表之前,必须通过eval_const_expressions运行它们。

这个函数在重写器中被调用,而不是在规划器中,因此可用的信息与在规划例程中的有点区别。parsetree是UPDATE或DELETE命令的分析树,而target_rte和target_relation描述目标外部表。

如果AddForeignUpdateTargets指针被设置为NULL,则不会有额外的目标表达式被加入(这将使得我们不可能实现DELETE操作,而UPDATE则还有可能是可行的,前提是FDW依赖一个未改变的主键来标识行)。

  • PlanForeignModify
1
2
3
4
5
List *
PlanForeignModify(PlannerInfo *root,
ModifyTable *plan,
Index resultRelation,
int subplan_index);

执行外部表上插入、更新或删除所需的任何附加规划动作。这个函数生成FDW私有信息,该信息将被附加到执行该更新动作的ModifyTable计划节点。这个私有信息的形式必须是一个List,并将会在执行阶段被传递给BeginForeignModify。

root是规划器关于该查询的全局信息。plan是ModifyTable计划节点,它除了fdwPrivLists属性之外是完整的。resultRelation通过目标外部表的范围表索引来标识它。subplan_index标识这是ModifyTable计划节点的哪个目标,从零开始计数;如果你希望索引到plan->plans或其他plan节点的子结构中,请使用它。

如果PlanForeignModify指针被设置为NULL,则不会有额外的计划时动作被执行,并且传递给BeginForeignModify的fdw_private列表也将为 NIL。

  • BeginForeignModify
1
2
3
4
5
6
void
BeginForeignModify (ModifyTableState *mtstate,
ResultRelInfo *rinfo,
List *fdw_private,
int subplan_index,
int eflags);

开始执行一个外部表修改操作。这个函数在执行器启动期间被调用。它应该执行任何先于实际表修改的初始化工作。随后,ExecForeignInsertExecForeignUpdateExecForeignDelete将被为每一个将被插入、更新或删除的元组调用。

mtstate是要被执行的ModifyTable计划节点的状态信息;通过这个结构可以得到关于规划和执行阶段的全局数据。rinfo是描述目标外部表的ResultRelInfo结构(ResultRelInfo的ri_FdwState属性用于FDW来存储它在此操作中需要的任何私有状态)。fdw_private包含PlanForeignModify生成的私有数据。subplan_index标识这是ModifyTable计划节点的哪个目标。eflags包含描述执行器对该计划节点操作模式的标志位。

注意当(eflags & EXEC_FLAG_EXPLAIN_ONLY)为真,这个函数不应执行任何外部可见的动作;它只应该做最少的工作来创建ExplainForeignModifyEndForeignModify可用的节点状态。

如果BeginForeignModify指针被设置为NULL,在执行器启动期间将不会采取任何动作。

  • ExecForeignInsert
1
2
3
4
5
TupleTableSlot *
ExecForeignInsert(EState *estate,
ResultRelInfo *rinfo,
TupleTableSlot *slot,
TupleTableSlot *planSlot);

插入一个元组到外部表。estate是查询的全局执行状态。rinfo是描述目标外部表的ResultRelInfo结构。slot包含要被插入的元组;它将匹配外部表的行类型定义。planSlot包含由ModifyTable计划节点的子计划生成的元组;它与slot不同,它可能包含额外的“junk”列(INSERT情况通常不关心planSlot,但是为了完整性还是在这里提供它)。

返回值可以是一个包含实际被插入的数据的槽(这可能会和所提供的数据不同,例如一个触发器动作的结果),或者为 NULL 表示实际没有插入行(还是触发器的结果)。被传入的slot可以被重用于这个目的。

在返回槽中的数据只有在INSERT查询具有一个RETURNING子句或者外部表具有一个AFTER ROW触发器时才被使用。触发器要求所有的列,但是 FDW 应该选择优化成根据RETURNING子句的内容返回某些或全部列。不管怎样,某些槽必须被返回来指示成功,否则查询报告的行计数将会是错误的。

如果ExecForeignInsert指针被设置为NULL,尝试向外部表插入将会失败并报告一个错误消息。

  • ExecForeignUpdate
1
2
3
4
5
TupleTableSlot *
ExecForeignUpdate(EState *estate,
ResultRelInfo *rinfo,
TupleTableSlot *slot,
TupleTableSlot *planSlot);

更新外部表中的一个元组。estate是查询的全局执行状态。rinfo是描述目标外部表的ResultRelInfo结构。slot包含元组的新数据;它将匹配外部表的行类型定义。planSlot包含由ModifyTable计划节点的子计划生成的元组;它与slot不同,它可能包含额外的“junk”列。特殊地,任何AddForeignUpdateTargets所要求的junk列在这个槽中都是有效的。

返回值可以是一个包含实际被更新的数据的槽(这可能会和所提供的数据不同,例如一个触发器动作的结果),或者为 NULL 表示实际没有更新行(还是触发器的结果)。被传入的slot可以被重用于这个目的。

在返回槽中的数据只有在UPDATE查询具有一个RETURNING子句或者外部表具有一个AFTER ROW触发器时才被使用。触发器要求所有的列,但是 FDW 应该选择优化成根据RETURNING子句的内容返回某些或全部列。不管怎样,某些槽必须被返回来指示成功,否则查询报告的行计数将会是错误的。

如果ExecForeignUpdate指针被设置为NULL,尝试更新外部表将会失败并报告一个错误消息。

  • ExecForeignDelete
1
2
3
4
5
TupleTableSlot *
ExecForeignDelete(EState *estate,
ResultRelInfo *rinfo,
TupleTableSlot *slot,
TupleTableSlot *planSlot);

从外部表删除一个元组。estate是查询的全局执行状态。rinfo是描述目标外部表的ResultRelInfo结构。slot在调用时不包含任何有用的东西,但是可以被用于保持被返回的元组。planSlot包含由ModifyTable计划节点的子计划生成的元组;特殊地,它将携带AddForeignUpdateTargets所要求的任意junk列。junk列被用来标识要被删除的元组。

返回值可以是一个包含实际被删除的数据的槽,或者为 NULL 表示实际没有删除行(还是触发器的结果)。被传入的slot可以被重用于这个目的。

在返回槽中的数据只有在DELETE查询具有一个RETURNING子句或者外部表具有一个AFTER ROW触发器时才被使用。触发器要求所有的列,但是 FDW 应该选择优化成根据RETURNING子句的内容返回某些或全部列。不管怎样,某些槽必须被返回来指示成功,否则查询报告的行计数将会是错误的。

如果ExecForeignDelete指针被设置为NULL,尝试从外部表中删除将会失败并报告一个错误消息。

  • EndForeignModify
1
2
3
void
EndForeignModify(EState *estate,
ResultRelInfo *rinfo);

结束表更新并释放资源。通常释放palloc的内存并不重要,但是打开的文件和到远程服务器的连接等应当被清除。

如果EndForeignModify指针被设置为NULL,在执行器关闭期间不会采取任何动作。



通过INSERT或COPY FROM插入分区表的元组将路由到分区。如果FDW支持可路由的外表分区,它还应提供以下回调函数。在外部表上执行COPY FROM时也会调用这些函数:

  • BeginForeignInsert
1
2
3
void
BeginForeignInsert(ModifyTableState *mtstate,
ResultRelInfo *rinfo);

开始在外表上执行插入操作。在将第一个元组插入到外表中之前,当它是为元组路由选择的分区和在COPY FROM命令中指定的目标时,就会调用此例程。它应该在实际插入之前执行所需的任何初始化。随后,将调用ExecForeignInsert以将每个元组插入到外表中。

mtstate是要被执行的ModifyTable计划节点的状态信息;通过这个结构可以得到关于规划和执行阶段的全局数据。rinfo是描述目标外部表的ResultRelInfo结构(ResultRelInfo的ri_FdwState属性用于FDW来存储它在此操作中需要的任何私有状态)。

当通过COPY FROM命令调用此方法时,不提供mtstate中与计划相关的全局数据,并且随后为每个插入的元组调用的ExecForeignInsert的planSlot参数为NULL,无论外表是为元组路由选择的分区还是在命令中指定的目标。

如果BeginForeignInsert指针设置为NULL,则不会对初始化执行任何操作。

  • EndForeignInsert
1
2
3
void
EndForeignInsert(EState *estate,
ResultRelInfo *rinfo);

结束表插入并释放资源。通常释放palloc的内存并不重要,但是打开的文件和到远程服务器的连接等应当被清除。

如果EndForeignInsert指针被设置为NULL,在执行器关闭期间不会采取任何动作。


  • IsForeignRelUpdatable
1
2
int
IsForeignRelUpdatable(Relation rel);

报告指定的外部表支持哪些更新操作。返回值应该是一个规则事件编号的位掩码,它指示了哪些操作被外部表支持,它使用CmdType枚举,即:(1 << CMD_UPDATE) = 4表示UPDATE、 (1 << CMD_INSERT) = 8表示INSERT以及 (1 << CMD_DELETE) = 16表示DELETE。

如果IsForeignRelUpdatable指针被设置为NULL,而FDW提供了ExecForeignInsertExecForeignUpdateExecForeignDelete,则外部表分别被假定为可插入、可更新或可删除。



一些对于外部表的插入、更新和删除可以通过实现另一组接口来优化。普通的插入、更新和删除接口会从远程服务器取得行,然后一次修改其中一行。在某些情况下,这种逐行的方式是必要的,但是可能效率不高。如果有可能让外部服务器判断哪些行可以直接修改而无需先检索它们并且没有本地触发器会影响该操作,那么可以让整个操作在远程服务器上执行。下面介绍的接口能让这种做法变成可能。

  • PlanDirectModify
1
2
3
4
5
bool
PlanDirectModify(PlannerInfo *root,
ModifyTable *plan,
Index resultRelation,
int subplan_index);

决定在远程服务器上执行直接修改是否安全。如果安全,执行所需的规划动作然后返回true,否则返回false。这个可选的函数在查询规划期间被调用。如果这个函数成功,在执行阶段将会调用BeginDirectModifyIterateDirectModifyEndDirectModify。否则,对表的修改将采用上文描述的表更新函数来执行。参数和PlanForeignModify的相同。

要在远程服务器上执行直接修改,这个函数必须用一个ForeignScan计划节点(它在远程服务器上执行直接修改)重写目标子计划。ForeignScan的operation域必须被合适地设置为CmdType枚举值,即CMD_UPDATE表示UPDATE、CMD_INSERT表示INSERT而CMD_DELETE表示DELETE。

如果PlanDirectModify指针被设置为NULL,不会尝试在远程服务器上执行直接修改。

  • BeginDirectModify
1
2
3
void
BeginDirectModify(ForeignScanState *node,
int eflags);

准备在远程服务器上执行一次直接修改。这个函数会在执行器启动时被调用。它应该执行直接修改所需的任何初始化工作(应该在第一次IterateDirectModify调用之前完成)。ForeignScanState节点已经被创建,但是它的fdw_state属性仍然为 NULL。有关要被修改的表的信息可以访问ForeignScanState节点(具体地,从底层的ForeignScan计划节点,它包含了PlanDirectModify提供的 FDW-私有信息)。eflags包含描述执行器对于这个计划节点操作模式的标志位。

注意当(eflags & EXEC_FLAG_EXPLAIN_ONLY)为真时,这个函数不应该执行任何外部可见的动作。它应当只做最少的工作让该节点状态对ExplainDirectModifyEndDirectModify有效。

如果BeginDirectModify指针被设置为NULL,不会尝试在远程服务器上执行直接修改。

  • IterateDirectModify
1
2
TupleTableSlot *
IterateDirectModify(ForeignScanState *node);

当INSERT、UPDATE或者DELETE查询没有RETURNING子句时,完成远程服务器上的直接修改后返回 NULL。当查询有该子句时,取出一个包含RETURNING计算所需数据的结果,用一个元组表槽返回它(节点的ScanTupleSlot应被用于这一目的)。实际被插入、更新或者删除的数据必须被存储在该节点的EState的es_result_relation_info->ri_projectReturning->pi_exprContext->ecxt_scantuple中。如果没有更多行可用,则返回 NULL。注意这个函数会在一个短期生存的内存上下文中被调用,该上下文会在两次调用之间被重置。如果需要一个长期存在的存储,可以在BeginDirectModify中创建一个内存上下文,或者使用该节点的EState中的es_query_cxt。

如果提供了fdw_scan_tlist目标列表,则被返回的行必须匹配它。否则,被返回的行必须匹配被更新的外部表的行类型。如果选择优化掉RETURNING计算不需要的列,应该在这些列的位置上插入空值,或者生成一个忽略这些列的fdw_scan_tlist列表。

不管该查询是否具有RETURNING子句,查询所报告的行计数必须由 FDW 本身增加。当查询没有该子句时,FDW 还必须为EXPLAIN ANALYZE情况下的ForeignScanState节点增加行计数。

如果IterateDirectModify指针被设置为NULL,不会尝试在远程服务器上执行直接修改。

  • EndDirectModify
1
2
void
EndDirectModify(ForeignScanState *node);

在远程服务器上的直接修改后进行清理。通常释放用 palloc 分配的内存并不重要,但是诸如打开的文件和到远程服务器的连接应该被清除。

如果EndDirectModify指针被设置为NULL,不会尝试在远程服务器上执行直接修改。





在外部表插入一条数据insert into t_fdw values(11,'1','2');
执行insert语句的调用顺序

使用gdb追踪

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
(gdb) b postgresAddForeignUpdateTargets
Breakpoint 1 at 0x7fee88320260: file postgres_fdw.c, line 1541.
(gdb) b postgresPlanForeignModify
Breakpoint 2 at 0x7fee883203a0: file postgres_fdw.c, line 1579.
(gdb) b postgresBeginForeignModify
Breakpoint 3 at 0x7fee88321e20: file postgres_fdw.c, line 1698.
(gdb) b postgresExecForeignInsert
Breakpoint 4 at 0x7fee88323000: file postgres_fdw.c, line 1750.
(gdb) b postgresExecForeignUpdate
Breakpoint 5 at 0x7fee88322e40: file postgres_fdw.c, line 1814.
(gdb) b postgresExecForeignDelete
Breakpoint 6 at 0x7fee88322c80: file postgres_fdw.c, line 1890.
(gdb) b postgresEndForeignModify
Breakpoint 7 at 0x7fee88321010: file postgres_fdw.c, line 1965.
(gdb) b postgresBeginForeignInsert
Breakpoint 8 at 0x7fee88321c10: file postgres_fdw.c, line 1982.
(gdb) b postgresEndForeignInsert
Breakpoint 9 at 0x7fee88320ff0: file postgres_fdw.c, line 2074.
(gdb) b postgresIsForeignRelUpdatable
Breakpoint 10 at 0x7fee8831e900: file postgres_fdw.c, line 2089.
(gdb) b postgresPlanDirectModify
Breakpoint 11 at 0x7fee883213b0: file postgres_fdw.c, line 2167.
(gdb) b postgresBeginDirectModify
Breakpoint 12 at 0x7fee8831fd80: file postgres_fdw.c, line 2360.
(gdb) b postgresIterateDirectModify
Breakpoint 13 at 0x7fee883227f0: file postgres_fdw.c, line 2479.
(gdb) b postgresEndDirectModify
Breakpoint 14 at 0x7fee8831fb60: file postgres_fdw.c, line 2523.

# 切换至psql,执行插入语句
fdw=# insert into t_fdw values(11,'1','2');
# (挂起)

# 切换回gdb
(gdb) c
Continuing.

Breakpoint 11, postgresPlanDirectModify (root=0x2113f08, plan=0x2115680, resultRelation=1,
subplan_index=0) at postgres_fdw.c:2167
2167 {
(gdb) bt
#0 postgresPlanDirectModify (root=0x2113f08, plan=0x2115680, resultRelation=1, subplan_index=0) at postgres_fdw.c:2167
#1 0x00000000006790a1 in make_modifytable (epqParam=<optimized out>, onconflict=<optimized out>, rowMarks=<optimized out>, returningLists=<optimized out>, withCheckOptionLists=<optimized out>, subroots=<optimized out>, subplans=<optimized out>, resultRelations=<optimized out>, partColsUpdated=<optimized out>, partitioned_rels=<optimized out>, nominalRelation=<optimized out>, canSetTag=<optimized out>, operation=<optimized out>, root=<optimized out>) at createplan.c:6680
#2 create_modifytable_plan (best_path=0x20635e0, root=<optimized out>) at createplan.c:2479
#3 create_plan_recurse (root=root@entry=0x2113f08, best_path=0x20635e0, flags=flags@entry=1) at createplan.c:492
#4 0x0000000000679dc9 in create_plan (root=root@entry=0x2113f08, best_path=<optimized out>) at createplan.c:327
#5 0x0000000000685264 in standard_planner (parse=0x20636f0, cursorOptions=256, boundParams=0x0) at planner.c:412
#6 0x000000000072178c in pg_plan_query (querytree=querytree@entry=0x20636f0, cursorOptions=cursorOptions@entry=256, boundParams=boundParams@entry=0x0) at postgres.c:809
#7 0x000000000072186e in pg_plan_queries (querytrees=<optimized out>, cursorOptions=cursorOptions@entry=256, boundParams=boundParams@entry=0x0) at postgres.c:875
#8 0x0000000000721cda in exec_simple_query (query_string=0x2062810 "insert into t_fdw values(11,'1','2');") at postgres.c:1050
#9 0x0000000000722e62 in PostgresMain (argc=<optimized out>, argv=argv@entry=0x208c528, dbname=0x208c410 "fdw", username=<optimized out>) at postgres.c:4153
#10 0x000000000047a861 in BackendRun (port=0x20843f0) at postmaster.c:4361
#11 BackendStartup (port=0x20843f0) at postmaster.c:4033
#12 ServerLoop () at postmaster.c:1706
#13 0x00000000006babb9 in PostmasterMain (argc=argc@entry=3, argv=argv@entry=0x205d3c0)
at postmaster.c:1379
#14 0x000000000047b2a1 in main (argc=3, argv=0x205d3c0) at main.c:228
(gdb) finish
Run till exit from #0 postgresPlanDirectModify (root=0x2113f08, plan=0x2115680,
resultRelation=1, subplan_index=0) at postgres_fdw.c:2167
make_modifytable (epqParam=<optimized out>, onconflict=<optimized out>,
rowMarks=<optimized out>, returningLists=<optimized out>,
withCheckOptionLists=<optimized out>, subroots=<optimized out>, subplans=<optimized out>,
resultRelations=<optimized out>, partColsUpdated=<optimized out>,
partitioned_rels=<optimized out>, nominalRelation=<optimized out>,
canSetTag=<optimized out>, operation=<optimized out>, root=<optimized out>)
at createplan.c:6681
6681 if (direct_modify)
Value returned is $15 = false
(gdb) finish
Run till exit from #0 make_modifytable (epqParam=<optimized out>, onconflict=<optimized out>,
rowMarks=<optimized out>, returningLists=<optimized out>,
withCheckOptionLists=<optimized out>, subroots=<optimized out>, subplans=<optimized out>,
resultRelations=<optimized out>, partColsUpdated=<optimized out>,
partitioned_rels=<optimized out>, nominalRelation=<optimized out>,
canSetTag=<optimized out>, operation=<optimized out>, root=<optimized out>)
at createplan.c:6681

Breakpoint 2, postgresPlanForeignModify (root=0x2113f08, plan=plan@entry=0x2115680,
resultRelation=1, subplan_index=subplan_index@entry=0) at postgres_fdw.c:1579
1579 {
(gdb) bt
#0 postgresPlanForeignModify (root=0x2113f08, plan=plan@entry=0x2115680, resultRelation=1, subplan_index=subplan_index@entry=0) at postgres_fdw.c:1579
#1 0x0000000000677001 in make_modifytable (epqParam=<optimized out>, onconflict=<optimized out>, rowMarks=<optimized out>, returningLists=<optimized out>, withCheckOptionLists=<optimized out>, subroots=<optimized out>, subplans=<optimized out>, resultRelations=<optimized out>, partColsUpdated=<optimized out>, partitioned_rels=<optimized out>, nominalRelation=<optimized out>, canSetTag=<optimized out>, operation=<optimized out>, root=<optimized out>) at createplan.c:6687
#2 create_modifytable_plan (best_path=0x20635e0, root=<optimized out>) at createplan.c:2479
#3 create_plan_recurse (root=root@entry=0x2113f08, best_path=0x20635e0, flags=flags@entry=1) at createplan.c:492
#4 0x0000000000679dc9 in create_plan (root=root@entry=0x2113f08, best_path=<optimized out>) at createplan.c:327
#5 0x0000000000685264 in standard_planner (parse=0x20636f0, cursorOptions=256, boundParams=0x0) at planner.c:412
#6 0x000000000072178c in pg_plan_query (querytree=querytree@entry=0x20636f0, cursorOptions=cursorOptions@entry=256, boundParams=boundParams@entry=0x0) at postgres.c:809
#7 0x000000000072186e in pg_plan_queries (querytrees=<optimized out>, cursorOptions=cursorOptions@entry=256, boundParams=boundParams@entry=0x0) at postgres.c:875
#8 0x0000000000721cda in exec_simple_query (query_string=0x2062810 "insert into t_fdw values(12,'1','2');") at postgres.c:1050
#9 0x0000000000722e62 in PostgresMain (argc=<optimized out>, argv=argv@entry=0x208c528, dbname=0x208c410 "fdw", username=<optimized out>) at postgres.c:4153
#10 0x000000000047a861 in BackendRun (port=0x20843f0) at postmaster.c:4361
#11 BackendStartup (port=0x20843f0) at postmaster.c:4033
#12 ServerLoop () at postmaster.c:1706
#13 0x00000000006babb9 in PostmasterMain (argc=argc@entry=3, argv=argv@entry=0x205d3c0) at postmaster.c:1379
#14 0x000000000047b2a1 in main (argc=3, argv=0x205d3c0) at main.c:228
(gdb) finish
Run till exit from #0 postgresPlanForeignModify (root=0x2113f08, plan=plan@entry=0x2115680,
resultRelation=1, subplan_index=subplan_index@entry=0) at postgres_fdw.c:1579
0x0000000000677001 in make_modifytable (epqParam=<optimized out>, onconflict=<optimized out>,
rowMarks=<optimized out>, returningLists=<optimized out>,
withCheckOptionLists=<optimized out>, subroots=<optimized out>, subplans=<optimized out>,
resultRelations=<optimized out>, partColsUpdated=<optimized out>,
partitioned_rels=<optimized out>, nominalRelation=<optimized out>,
canSetTag=<optimized out>, operation=<optimized out>, root=<optimized out>)
at createplan.c:6687
6687 fdw_private = fdwroutine->PlanForeignModify(subroot, node, rti, i);
Value returned is $16 = (List *) 0x218b810
(gdb) finish
Run till exit from #0 0x0000000000677001 in make_modifytable (epqParam=<optimized out>,
onconflict=<optimized out>, rowMarks=<optimized out>, returningLists=<optimized out>,
withCheckOptionLists=<optimized out>, subroots=<optimized out>, subplans=<optimized out>,
resultRelations=<optimized out>, partColsUpdated=<optimized out>,
partitioned_rels=<optimized out>, nominalRelation=<optimized out>,
canSetTag=<optimized out>, operation=<optimized out>, root=<optimized out>)
at createplan.c:6687
create_plan_recurse (root=root@entry=0x2113f08, best_path=<optimized out>, flags=flags@entry=1)
at createplan.c:492
492 plan = (Plan *) create_modifytable_plan(root,
(gdb) finish
Run till exit from #0 create_plan_recurse (root=root@entry=0x2113f08,
best_path=<optimized out>, flags=flags@entry=1) at createplan.c:492
create_plan (root=root@entry=0x2113f08, best_path=<optimized out>) at createplan.c:336
336 if (!IsA(plan, ModifyTable))
Value returned is $17 = (Plan *) 0x2115680
(gdb) finish
Run till exit from #0 create_plan (root=root@entry=0x2113f08, best_path=<optimized out>)
at createplan.c:336
standard_planner (parse=0x20636f0, cursorOptions=256, boundParams=0x0) at planner.c:418
418 if (cursorOptions & CURSOR_OPT_SCROLL)
Value returned is $18 = (Plan *) 0x2115680
(gdb) finish
Run till exit from #0 standard_planner (parse=0x20636f0, cursorOptions=256, boundParams=0x0)
at planner.c:418
pg_plan_query (querytree=querytree@entry=0x20636f0, cursorOptions=cursorOptions@entry=256,
boundParams=boundParams@entry=0x0) at postgres.c:811
811 if (log_planner_stats)
Value returned is $19 = (PlannedStmt *) 0x218bb60
(gdb) finish
Run till exit from #0 pg_plan_query (querytree=querytree@entry=0x20636f0,
cursorOptions=cursorOptions@entry=256, boundParams=boundParams@entry=0x0) at postgres.c:811
0x000000000072186e in pg_plan_queries (querytrees=<optimized out>,
cursorOptions=cursorOptions@entry=256, boundParams=boundParams@entry=0x0) at postgres.c:875
875 stmt = pg_plan_query(query, cursorOptions, boundParams);
Value returned is $20 = (PlannedStmt *) 0x218bb60
(gdb) finish
Run till exit from #0 0x000000000072186e in pg_plan_queries (querytrees=<optimized out>,
cursorOptions=cursorOptions@entry=256, boundParams=boundParams@entry=0x0) at postgres.c:875
0x0000000000721cda in exec_simple_query (
query_string=0x2062810 "insert into t_fdw values(12,'1','2');") at postgres.c:1050
1050 plantree_list = pg_plan_queries(querytree_list,
Value returned is $21 = (List *) 0x218bc90
(gdb) finish
Run till exit from #0 0x0000000000721cda in exec_simple_query (
query_string=0x2062810 "insert into t_fdw values(12,'1','2');") at postgres.c:1050

Breakpoint 10, postgresIsForeignRelUpdatable (rel=0x7feed1ad1358) at postgres_fdw.c:2089
2089 {
(gdb) bt
#0 postgresIsForeignRelUpdatable (rel=0x7feed1ad1358) at postgres_fdw.c:2089
#1 0x00000000005f0820 in CheckValidResultRel (resultRelInfo=resultRelInfo@entry=0x20885c0, operation=operation@entry=CMD_INSERT) at execMain.c:1187
#2 0x00000000006123a8 in ExecInitModifyTable (node=node@entry=0x2115680, estate=estate@entry=0x2088380, eflags=eflags@entry=0) at nodeModifyTable.c:2242
#3 0x00000000005f6838 in ExecInitNode (node=node@entry=0x2115680, estate=estate@entry=0x2088380, eflags=eflags@entry=0) at execProcnode.c:174
#4 0x00000000005f11c5 in InitPlan (eflags=0, queryDesc=<optimized out>) at execMain.c:1049
#5 standard_ExecutorStart (queryDesc=<optimized out>, eflags=0) at execMain.c:264
#6 0x0000000000724d26 in ProcessQuery (plan=<optimized out>, sourceText=0x2062810 "insert into t_fdw values(12,'1','2');", params=0x0, queryEnv=0x0, dest=0x218bcc0, completionTag=0x7ffd2229fcd0 "") at pquery.c:156
#7 0x0000000000724f61 in PortalRunMulti (portal=portal@entry=0x20c7f10, isTopLevel=isTopLevel@entry=true, setHoldSnapshot=setHoldSnapshot@entry=false, dest=dest@entry=0x218bcc0, altdest=altdest@entry=0x218bcc0, completionTag=completionTag@entry=0x7ffd2229fcd0 "") at pquery.c:1286
#8 0x0000000000725a1c in PortalRun (portal=portal@entry=0x20c7f10, count=count@entry=9223372036854775807, isTopLevel=isTopLevel@entry=true, run_once=run_once@entry=true, dest=dest@entry=0x218bcc0, altdest=altdest@entry=0x218bcc0, completionTag=completionTag@entry=0x7ffd2229fcd0 "") at pquery.c:799
#9 0x0000000000721bb7 in exec_simple_query (query_string=0x2062810 "insert into t_fdw values(12,'1','2');") at postgres.c:1122
#10 0x0000000000722e62 in PostgresMain (argc=<optimized out>, argv=argv@entry=0x208c528, dbname=0x208c410 "fdw", username=<optimized out>) at postgres.c:4153
#11 0x000000000047a861 in BackendRun (port=0x20843f0) at postmaster.c:4361
#12 BackendStartup (port=0x20843f0) at postmaster.c:4033
#13 ServerLoop () at postmaster.c:1706
#14 0x00000000006babb9 in PostmasterMain (argc=argc@entry=3, argv=argv@entry=0x205d3c0) at postmaster.c:1379
#15 0x000000000047b2a1 in main (argc=3, argv=0x205d3c0) at main.c:228
(gdb) finish
Run till exit from #0 postgresIsForeignRelUpdatable (rel=0x7feed1ad1358) at postgres_fdw.c:2089
CheckValidResultRel (resultRelInfo=resultRelInfo@entry=0x20885c0,
operation=operation@entry=CMD_INSERT) at execMain.c:1186
1186 if (fdwroutine->IsForeignRelUpdatable != NULL &&
Value returned is $22 = 28
(gdb) finish
Run till exit from #0 CheckValidResultRel (resultRelInfo=resultRelInfo@entry=0x20885c0,
operation=operation@entry=CMD_INSERT) at execMain.c:1186
ExecInitModifyTable (node=node@entry=0x2115680, estate=estate@entry=0x2088380,
eflags=eflags@entry=0) at nodeModifyTable.c:2253
2253 if (resultRelInfo->ri_RelationDesc->rd_rel->relhasindex &&
(gdb) finish
Run till exit from #0 ExecInitModifyTable (node=node@entry=0x2115680,
estate=estate@entry=0x2088380, eflags=eflags@entry=0) at nodeModifyTable.c:2253

Breakpoint 3, postgresBeginForeignModify (mtstate=0x20888e0, resultRelInfo=0x20885c0,
fdw_private=0x218b810, subplan_index=0, eflags=0) at postgres_fdw.c:1698
1698 {
(gdb) bt
#0 postgresBeginForeignModify (mtstate=0x20888e0, resultRelInfo=0x20885c0, fdw_private=0x218b810, subplan_index=0, eflags=0) at postgres_fdw.c:1698
#1 0x0000000000612458 in ExecInitModifyTable (node=node@entry=0x2115680, estate=estate@entry=0x2088380, eflags=eflags@entry=0) at nodeModifyTable.c:2280
#2 0x00000000005f6838 in ExecInitNode (node=node@entry=0x2115680, estate=estate@entry=0x2088380, eflags=eflags@entry=0) at execProcnode.c:174
#3 0x00000000005f11c5 in InitPlan (eflags=0, queryDesc=<optimized out>) at execMain.c:1049
#4 standard_ExecutorStart (queryDesc=<optimized out>, eflags=0) at execMain.c:264
#5 0x0000000000724d26 in ProcessQuery (plan=<optimized out>, sourceText=0x2062810 "insert into t_fdw values(13,'1','2');", params=0x0, queryEnv=0x0, dest=0x218bcc0, completionTag=0x7ffd2229fcd0 "") at pquery.c:156
#6 0x0000000000724f61 in PortalRunMulti (portal=portal@entry=0x20c7f10, isTopLevel=isTopLevel@entry=true, setHoldSnapshot=setHoldSnapshot@entry=false, dest=dest@entry=0x218bcc0, altdest=altdest@entry=0x218bcc0, completionTag=completionTag@entry=0x7ffd2229fcd0 "") at pquery.c:1286
#7 0x0000000000725a1c in PortalRun (portal=portal@entry=0x20c7f10, count=count@entry=9223372036854775807, isTopLevel=isTopLevel@entry=true, run_once=run_once@entry=true, dest=dest@entry=0x218bcc0, altdest=altdest@entry=0x218bcc0, completionTag=completionTag@entry=0x7ffd2229fcd0 "") at pquery.c:799
#8 0x0000000000721bb7 in exec_simple_query (query_string=0x2062810 "insert into t_fdw values(13,'1','2');") at postgres.c:1122
#9 0x0000000000722e62 in PostgresMain (argc=<optimized out>, argv=argv@entry=0x208c528, dbname=0x208c410 "fdw", username=<optimized out>) at postgres.c:4153
#10 0x000000000047a861 in BackendRun (port=0x20843f0) at postmaster.c:4361
#11 BackendStartup (port=0x20843f0) at postmaster.c:4033
#12 ServerLoop () at postmaster.c:1706
#13 0x00000000006babb9 in PostmasterMain (argc=argc@entry=3, argv=argv@entry=0x205d3c0) at postmaster.c:1379
#14 0x000000000047b2a1 in main (argc=3, argv=0x205d3c0) at main.c:228
(gdb) finish
Run till exit from #0 postgresBeginForeignModify (mtstate=0x20888e0, resultRelInfo=0x20885c0,
fdw_private=0x218b810, subplan_index=0, eflags=0) at postgres_fdw.c:1698
ExecInitModifyTable (node=node@entry=0x2115680, estate=estate@entry=0x2088380,
eflags=eflags@entry=0) at nodeModifyTable.c:2231
2231 foreach(l, node->plans)
(gdb) finish
Run till exit from #0 ExecInitModifyTable (node=node@entry=0x2115680,
estate=estate@entry=0x2088380, eflags=eflags@entry=0) at nodeModifyTable.c:2231
0x00000000005f6838 in ExecInitNode (node=node@entry=0x2115680, estate=estate@entry=0x2088380,
eflags=eflags@entry=0) at execProcnode.c:174
174 result = (PlanState *) ExecInitModifyTable((ModifyTable *) node,
Value returned is $23 = (ModifyTableState *) 0x20888e0
(gdb) finish
Run till exit from #0 0x00000000005f6838 in ExecInitNode (node=node@entry=0x2115680,
estate=estate@entry=0x2088380, eflags=eflags@entry=0) at execProcnode.c:174
InitPlan (eflags=0, queryDesc=<optimized out>) at execMain.c:1054
1054 tupType = ExecGetResultType(planstate);
Value returned is $24 = (PlanState *) 0x20888e0
(gdb) finish
Run till exit from #0 InitPlan (eflags=0, queryDesc=<optimized out>) at execMain.c:1054
266 MemoryContextSwitchTo(oldcontext);
(gdb) finish
Run till exit from #0 standard_ExecutorStart (queryDesc=<optimized out>, eflags=0)
at execMain.c:266
ProcessQuery (plan=<optimized out>,
sourceText=0x2062810 "insert into t_fdw values(12,'1','2');", params=0x0, queryEnv=0x0,
dest=0x218bcc0, completionTag=0x7ffd2229fcd0 "") at pquery.c:161
161 ExecutorRun(queryDesc, ForwardScanDirection, 0L, true);
(gdb) finish
Run till exit from #0 ProcessQuery (plan=<optimized out>,
sourceText=0x2062810 "insert into t_fdw values(12,'1','2');", params=0x0, queryEnv=0x0,
dest=0x218bcc0, completionTag=0x7ffd2229fcd0 "") at pquery.c:161

Breakpoint 4, postgresExecForeignInsert (estate=0x2088380, resultRelInfo=0x20885c0,
slot=0x2089628, planSlot=0x2089628) at postgres_fdw.c:1750
1750 {
(gdb) bt
#0 postgresExecForeignInsert (estate=0x2088380, resultRelInfo=0x20885c0, slot=0x2089628, planSlot=0x2089628) at postgres_fdw.c:1750
#1 0x0000000000610d8b in ExecInsert (mtstate=mtstate@entry=0x20888e0, slot=0x2089628, planSlot=planSlot@entry=0x2089628, estate=estate@entry=0x2088380, canSetTag=<optimized out>) at nodeModifyTable.c:346
#2 0x0000000000612119 in ExecModifyTable (pstate=0x20888e0) at nodeModifyTable.c:2126
#3 0x00000000005f000a in ExecProcNode (node=0x20888e0) at ../../../src/include/executor/executor.h:237
#4 ExecutePlan (execute_once=<optimized out>, dest=0x218bcc0, direction=<optimized out>, numberTuples=0, sendTuples=false, operation=CMD_INSERT, use_parallel_mode=<optimized out>, planstate=0x20888e0, estate=0x2088380) at execMain.c:1726
#5 standard_ExecutorRun (queryDesc=0x21413a0, direction=<optimized out>, count=0, execute_once=<optimized out>) at execMain.c:363
#6 0x0000000000724d3a in ProcessQuery (plan=<optimized out>, sourceText=0x2062810 "insert into t_fdw values(12,'1','2');", params=0x0, queryEnv=0x0, dest=0x218bcc0, completionTag=0x7ffd2229fcd0 "") at pquery.c:161
#7 0x0000000000724f61 in PortalRunMulti (portal=portal@entry=0x20c7f10, isTopLevel=isTopLevel@entry=true, setHoldSnapshot=setHoldSnapshot@entry=false, dest=dest@entry=0x218bcc0, altdest=altdest@entry=0x218bcc0, completionTag=completionTag@entry=0x7ffd2229fcd0 "") at pquery.c:1286
#8 0x0000000000725a1c in PortalRun (portal=portal@entry=0x20c7f10, count=count@entry=9223372036854775807, isTopLevel=isTopLevel@entry=true, run_once=run_once@entry=true, dest=dest@entry=0x218bcc0, altdest=altdest@entry=0x218bcc0, completionTag=completionTag@entry=0x7ffd2229fcd0 "") at pquery.c:799
#9 0x0000000000721bb7 in exec_simple_query (query_string=0x2062810 "insert into t_fdw values(12,'1','2');") at postgres.c:1122
#10 0x0000000000722e62 in PostgresMain (argc=<optimized out>, argv=argv@entry=0x208c528, dbname=0x208c410 "fdw", username=<optimized out>) at postgres.c:4153
#11 0x000000000047a861 in BackendRun (port=0x20843f0) at postmaster.c:4361
#12 BackendStartup (port=0x20843f0) at postmaster.c:4033
#13 ServerLoop () at postmaster.c:1706
#14 0x00000000006babb9 in PostmasterMain (argc=argc@entry=3, argv=argv@entry=0x205d3c0) at postmaster.c:1379
#15 0x000000000047b2a1 in main (argc=3, argv=0x205d3c0) at main.c:228
(gdb) finish
Run till exit from #0 postgresExecForeignInsert (estate=0x2088380, resultRelInfo=0x20885c0,
slot=0x2089628, planSlot=0x2089628) at postgres_fdw.c:1750
ExecInsert (mtstate=mtstate@entry=0x20888e0, slot=0x2089628, planSlot=planSlot@entry=0x2089628,
estate=estate@entry=0x2088380, canSetTag=<optimized out>) at nodeModifyTable.c:351
351 if (slot == NULL) /* "do nothing" */
Value returned is $25 = (TupleTableSlot *) 0x2089628
(gdb) finish
Run till exit from #0 ExecInsert (mtstate=mtstate@entry=0x20888e0, slot=0x2089628,
planSlot=planSlot@entry=0x2089628, estate=estate@entry=0x2088380, canSetTag=<optimized out>)
at nodeModifyTable.c:351
0x0000000000612119 in ExecModifyTable (pstate=0x20888e0) at nodeModifyTable.c:2126
2126 slot = ExecInsert(node, slot, planSlot,
Value returned is $26 = (TupleTableSlot *) 0x0
(gdb) finish
Run till exit from #0 0x0000000000612119 in ExecModifyTable (pstate=0x20888e0)
at nodeModifyTable.c:2126
ExecutePlan (execute_once=<optimized out>, dest=0x218bcc0, direction=<optimized out>,
numberTuples=0, sendTuples=false, operation=CMD_INSERT, use_parallel_mode=<optimized out>,
planstate=0x20888e0, estate=0x2088380) at execMain.c:1732
1732 if (TupIsNull(slot))
Value returned is $27 = (TupleTableSlot *) 0x0
(gdb) finish
Run till exit from #0 ExecutePlan (execute_once=<optimized out>, dest=0x218bcc0,
direction=<optimized out>, numberTuples=0, sendTuples=false, operation=CMD_INSERT,
use_parallel_mode=<optimized out>, planstate=0x20888e0, estate=0x2088380) at execMain.c:1732
377 if (sendTuples)
(gdb) finish
Run till exit from #0 standard_ExecutorRun (queryDesc=0x21413a0, direction=<optimized out>,
count=0, execute_once=<optimized out>) at execMain.c:377
ProcessQuery (plan=<optimized out>,
sourceText=0x2062810 "insert into t_fdw values(12,'1','2');", params=0x0, queryEnv=0x0,
dest=0x218bcc0, completionTag=0x7ffd2229fcd0 "") at pquery.c:166
166 if (completionTag)
(gdb) finish
Run till exit from #0 ProcessQuery (plan=<optimized out>,
sourceText=0x2062810 "insert into t_fdw values(12,'1','2');", params=0x0, queryEnv=0x0,
dest=0x218bcc0, completionTag=0x7ffd2229fcd0 "") at pquery.c:166

Breakpoint 7, postgresEndForeignModify (estate=0x2088380, resultRelInfo=0x20885c0)
at postgres_fdw.c:1965
1965 PgFdwModifyState *fmstate = (PgFdwModifyState *) resultRelInfo->ri_FdwState;
(gdb) bt
#0 postgresEndForeignModify (estate=0x2088380, resultRelInfo=0x20885c0) at postgres_fdw.c:1965
#1 0x0000000000612c7b in ExecEndModifyTable (node=0x20888e0) at nodeModifyTable.c:2664
#2 0x00000000005f16ad in ExecEndPlan (estate=0x2088380, planstate=<optimized out>) at execMain.c:1612
#3 standard_ExecutorEnd (queryDesc=0x21413a0) at execMain.c:495
#4 0x0000000000724d90 in ProcessQuery (plan=<optimized out>, sourceText=0x2062810 "insert into t_fdw values(12,'1','2');", params=0x0, queryEnv=0x0, dest=0x218bcc0, completionTag=0x7ffd2229fcd0 "INSERT 0 1") at pquery.c:206
#5 0x0000000000724f61 in PortalRunMulti (portal=portal@entry=0x20c7f10, isTopLevel=isTopLevel@entry=true, setHoldSnapshot=setHoldSnapshot@entry=false, dest=dest@entry=0x218bcc0, altdest=altdest@entry=0x218bcc0, completionTag=completionTag@entry=0x7ffd2229fcd0 "INSERT 0 1") at pquery.c:1286
#6 0x0000000000725a1c in PortalRun (portal=portal@entry=0x20c7f10, count=count@entry=9223372036854775807, isTopLevel=isTopLevel@entry=true, run_once=run_once@entry=true, dest=dest@entry=0x218bcc0, altdest=altdest@entry=0x218bcc0, completionTag=completionTag@entry=0x7ffd2229fcd0 "INSERT 0 1") at pquery.c:799
#7 0x0000000000721bb7 in exec_simple_query (query_string=0x2062810 "insert into t_fdw values(12,'1','2');") at postgres.c:1122
#8 0x0000000000722e62 in PostgresMain (argc=<optimized out>, argv=argv@entry=0x208c528, dbname=0x208c410 "fdw", username=<optimized out>) at postgres.c:4153
#9 0x000000000047a861 in BackendRun (port=0x20843f0) at postmaster.c:4361
#10 BackendStartup (port=0x20843f0) at postmaster.c:4033
#11 ServerLoop () at postmaster.c:1706
#12 0x00000000006babb9 in PostmasterMain (argc=argc@entry=3, argv=argv@entry=0x205d3c0) at postmaster.c:1379
#13 0x000000000047b2a1 in main (argc=3, argv=0x205d3c0) at main.c:228

# 后续执行结束
...

在外部表更新数据 update t_fdw set c1='11' where id=11;

在外部表删除数据,使用gdb跟踪调用流程,发现和update语句的流程是一样的,查看相关代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
/*
* postgresIterateDirectModify
* Execute a direct foreign table modification
*/
static TupleTableSlot *
postgresIterateDirectModify(ForeignScanState *node)
{
PgFdwDirectModifyState *dmstate = (PgFdwDirectModifyState *) node->fdw_state;
EState *estate = node->ss.ps.state;
ResultRelInfo *resultRelInfo = estate->es_result_relation_info;

/*
* If this is the first call after Begin, execute the statement.
*/
if (dmstate->num_tuples == -1)
execute_dml_stmt(node);

/*
* If the local query doesn't specify RETURNING, just clear tuple slot.
*/
if (!resultRelInfo->ri_projectReturning)
{
TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
Instrumentation *instr = node->ss.ps.instrument;

Assert(!dmstate->has_returning);

/* Increment the command es_processed count if necessary. */
if (dmstate->set_processed)
estate->es_processed += dmstate->num_tuples;

/* Increment the tuple count for EXPLAIN ANALYZE if necessary. */
if (instr)
instr->tuplecount += dmstate->num_tuples;

return ExecClearTuple(slot);
}

/*
* Get the next RETURNING tuple.
*/
return get_returning_data(node);
}

/*
* Execute a direct UPDATE/DELETE statement.
*/
static void
execute_dml_stmt(ForeignScanState *node)
{
PgFdwDirectModifyState *dmstate = (PgFdwDirectModifyState *) node->fdw_state;
ExprContext *econtext = node->ss.ps.ps_ExprContext;
int numParams = dmstate->numParams;
const char **values = dmstate->param_values;

/*
* Construct array of query parameter values in text format.
*/
if (numParams > 0)
process_query_params(econtext,
dmstate->param_flinfo,
dmstate->param_exprs,
values);

/*
* Notice that we pass NULL for paramTypes, thus forcing the remote server
* to infer types for all parameters. Since we explicitly cast every
* parameter (see deparse.c), the "inference" is trivial and will produce
* the desired result. This allows us to avoid assuming that the remote
* server has the same OIDs we do for the parameters' types.
*/
if (!PQsendQueryParams(dmstate->conn, dmstate->query, numParams,
NULL, values, NULL, NULL, 0))
pgfdw_report_error(ERROR, NULL, dmstate->conn, false, dmstate->query);

/*
* Get the result, and check for success.
*
* We don't use a PG_TRY block here, so be careful not to throw error
* without releasing the PGresult.
*/
dmstate->result = pgfdw_get_result(dmstate->conn, dmstate->query);
if (PQresultStatus(dmstate->result) !=
(dmstate->has_returning ? PGRES_TUPLES_OK : PGRES_COMMAND_OK))
pgfdw_report_error(ERROR, dmstate->result, dmstate->conn, true,
dmstate->query);

/* Get the number of rows affected. */
if (dmstate->has_returning)
dmstate->num_tuples = PQntuples(dmstate->result);
else
dmstate->num_tuples = atoi(PQcmdTuples(dmstate->result));
}

可以看到,postgresIterateDirectModify 调用了execute_dml_stmt执行直接UPDATE/DELETE语句的。