问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

今天我很高兴能为大家教单词,请大家跟我一起读。 求英语翻译,准确点!

发布网友 发布时间:2022-04-10 11:57

我来回答

7个回答

懂视网 时间:2022-04-10 16:19

5.ModifyTable节点

先看一个ModifyTable节点的例子:

postgres=# explain update test_01 set id = 5 where name = 'xxx';
    QUERY PLAN
---------------------------------------------------------------
 Update on test_01 (cost=0.00..23.75 rows=6 width=48)
 -> Seq Scan on test_01 (cost=0.00..23.75 rows=6 width=48)
  Filter: ((name)::text = 'xxx'::text)
(3 rows)

你可能疑惑为啥上面的查询计划里面没有"ModifyTable"这样的字眼,下面是explain.c文件中的一段:

case T_ModifyTable:
  sname = "ModifyTable";
  switch (((ModifyTable *) plan)->operation)
  {
  case CMD_INSERT:
   pname = operation = "Insert";
   break;
  case CMD_UPDATE:
   pname = operation = "Update";
   break;
  case CMD_DELETE:
   pname = operation = "Delete";
   break;
  default:
   pname = "???";
   break;
  }
  break;

由此我们可以看到,对于ModifyTable节点,explain会判断是增删改中的哪一种从而作出判断。所以当在explain中看到INSERT、Update和Delete时,我们就知道这是走了ModifyTable节点。

那这里,我们还要再解释ModifyTable节点么?解释下吧:

 * Apply rows produced by subplan(s) to result table(s),
 * by inserting, updating, or deleting.

也就是说,我先从下层的subplan中获得rows,然后根据命令类型选择是insert, update还是delete操作。所以我们可以知道,这是一个顶层节点,它下面是查询节点(就是SELECT)。这也符合我们以前一直说的,所有的增删改查其实都是SELECT!

typedef struct ModifyTable
{
 Plan plan;
 CmdType operation; /* INSERT, UPDATE, or DELETE */
 bool canSetTag; /* do we set the command tag/es_processed? */
 Index nominalRelation; /* Parent RT index for use of EXPLAIN */
 List *resultRelations; /* integer list of RT indexes */
 int  resultRelIndex; /* index of first resultRel in plan's list */
 List *plans;  /* plan(s) producing source data */
 List *withCheckOptionLists; /* per-target-table WCO lists */
 List *returningLists; /* per-target-table RETURNING tlists */
 List *fdwPrivLists; /* per-target-table FDW private data lists */
 List *rowMarks; /* PlanRowMarks (non-locking only) */
 int  epqParam; /* ID of Param for EvalPlanQual re-eval */
 OnConflictAction onConflictAction; /* ON CONFLICT action */
 List *arbiterIndexes; /* List of ON CONFLICT arbiter index OIDs */
 List *onConflictSet; /* SET for INSERT ON CONFLICT DO UPDATE */
 Node *onConflictWhere; /* WHERE for ON CONFLICT UPDATE */
 Index exclRelRTI; /* RTI of the EXCLUDED pseudo relation */
 List *exclRelTlist; /* tlist of the EXCLUDED pseudo relation */
} ModifyTable;

由于ModifyTable节点涉及的操作比较多,这里稍微解释下ModifyTable中的一些字段。



withCheckOptionLists字段

这个和视图相关,我们知道创建视图有这样的用法:

CREATE VIEW xxx_view AS query WITH CHECK OPTION

在postgres中,对创建语句中带有WITH CHECK OPTION的VIEW,在通过视图进行的操作(增删改),必须也能通过该视图看到操作后的结果。

也就是说:

对于INSERT,那么加的这条记录在视图查询后必须可以看到。

对于UPDATE,修改完的结果也必须能通过该视图看到。

对于DELETE,只能删除视图里有显示的记录。

因此对这一类操作,我们在操作表/视图的时候,要在(INSERT/UPDATE/DELETE的)WHERE条件中加上WITH OPTION中的条件。



returningLists字段

这个很简单,因为postgres的语法中有类似以下的用法:

DELETE FROM xxx_table WHERE condition RETURNING xxx;
UPDATE xxx_table SET a = '123' WHERE condition RETURNING xxx;
INSERT INTO xxx_table VALUES (somevalues) RETURNING xxx;

是的,postgres的RETURNING子句可以返回修改的行,所以对于含有RETURNING子句的查询,除了在对表中的数据进行INSERT/UPDATE/DELETE,还要额外返回一些行。即还要有额外的输出。



fdwPrivLists字段

Postgres支持访问外部数据库的嘛,所以这个字段提供对fdw的处理的支持。



rowMarks字段

这个和行锁相关,针对SELECT的LOCK子句:

FOR lock_strength [ OF table_name [, ...] ] [ NOWAIT | SKIP LOCKED ]

具体见这里:http://www.postgres.cn/docs/9.5/sql-select.html



onConflictAction、arbiterIndexes、arbiterIndexes和onConflictWhere字段

是的,对于INSERT操作,我们有以下语法(用于支持INSERT中发生的冲突):

INSERT INTO table_name VALUES (somevalues) ON CONFLICT [ conflict_target ] conflict_action

并且 conflict_action 是以下之一:

 DO NOTHING
 DO UPDATE SET { column_name = { expression | DEFAULT } |
   ( column_name [, ...] ) = ( { expression | DEFAULT } [, ...] ) |
   ( column_name [, ...] ) = ( sub-SELECT )
   } [, ...]
  [ WHERE condition ]

这样一看,很容易对的上了。



exclRelRTI、exclRelTlist字段

对于建表语句有以下子句:

EXCLUDE [ USING index_method ] ( exclude_element WITH operator [, ... ] ) index_parameters [ WHERE ( predicate ) ]

EXCLUDE子句指定一个排除约束,它保证如果任意两行在指定列或表达式上使用指定操作符进行比较,不是所有的比较都将会返回TRUE。具体见这里:

因此,你可以把这个字段看做是一个约束字段,在做更新操作时需要判断。



typedef struct ModifyTableState
{
 PlanState ps;  /* its first field is NodeTag */
 CmdType operation; /* INSERT, UPDATE, or DELETE */
 bool canSetTag; /* do we set the command tag/es_processed? */
 bool mt_done; /* are we done? */
 PlanState **mt_plans; /* subplans (one per target rel) */
 int  mt_nplans; /* number of plans in the array */
 int  mt_whichplan; /* which one is being executed (0..n-1) */
 ResultRelInfo *resultRelInfo; /* per-subplan target relations */
 List **mt_arowmarks; /* per-subplan ExecAuxRowMark lists */
 EPQState mt_epqstate; /* for evaluating EvalPlanQual rechecks */
 bool fireBSTriggers; /* do we need to fire stmt triggers? */
 OnConflictAction mt_onconflict; /* ON CONFLICT type */
 List *mt_arbiterindexes; /* unique index OIDs to arbitrate
      * taking alt path */
 TupleTableSlot *mt_existing; /* slot to store existing target tuple in */
 List *mt_excludedtlist; /* the excluded pseudo relation's
      * tlist */
 TupleTableSlot *mt_conflproj; /* CONFLICT ... SET ... projection
      * target */
} ModifyTableState;

那么对于ModifyTableState的一些字段,我们参照ModifyTable节点的解释,也能理解的差不多,这里不多说了。

下面进入正题:



ModifyTable节点的初始化由ExecInitModifyTable函数来做。该函数除了做一些基础的初始化操作外,针对我上面提到的那些字段,做了设置和初始化。说细一点的话就是:

  • (1)调用ExecInitNode函数对ModifyTable节点中的plans列表中的subplans节点进行初始化并将其结果保存到ModifyTableState结构的mt_plans字段中。在这一步中,同时也顺便做了这些事:验证了查询所涉及的target relations是否是合法的;打开这些target relations上的index,因为对于UPDATE/INSERT操作,我们同时还要对相应的索引进行操作(DELETE操作不删除索引,DELETE后遗留的index留给VACUUM去清理)。

  • (2)根据ModifyTable节点中的withCheckOptionLists字段初始化上面提到的WITH CHECK OPTION(如果存在的话)。初始化后保存在ModifyTableState结构的resultRelInfo字段的成员变量ri_WithCheckOptions和ri_WithCheckOptionExprs中。

  • (3)根据ModifyTable节点中的returningLists字段初始化上面提到的RETURNING子句(如果存在的话)并根据此构造返回的结果集的类型。如果returningLists字段为空,说明没有RETURNING子句。那么返回结果集的类型设置为NULL。

  • (4)如果存在ON CONFLICT DO UPDATE字段那么为他初始化目标列表Target List、投影条件resultRelInfo和过滤条件qual,结果保存在ModifyTableState结构的相应字段中。

  • (5)处理ModifyTable节点中的RowMark字段,结果保存在ModifyTableState结构的mt_arowmarks字段中。

  • (6)初始化junk filter。这个junk filter的由来是因为在plan阶段,我们会产生一些"中间信息"放在tuple中供Excutor使用。比如ctid,用来定位该元组放到磁盘文件的位置。但是当我们将元组写到磁盘时,我们不需要保存这个信息。那么这个信息相当于是冗余的了,我们需要用这个JunkFilter将其过滤和删除。

  • (7)我们知道我们在INSERT/UPDATE/DELETE时可能会涉及到trigger,这里设置trigger相关的slot,供后续函数调用。

  • (8)如果本ModifyTable节点不是顶层ModifyTable节点的话(上层还有ModifyTable节点),设置全局状态结构estate->es_auxmodifytables属性,做上标记。



  • ModifyTable节点的执行由ExecModifyTable函数执行。具体的来说:

  • (1)首先我们要记得可能有BEFORE STATEMENT triggers这玩意儿,顾名思义,就是要在STATEMENT执行之前执行这个trigger。如果存在,在进入正式的处理之前我们先要调用fireBSTriggers函数来处理它。

  • (2)接下来是一个大for循环。在这个for循环里面,程序调用ExecProcNode函数循环地从下层节点中读取元组。需要注意的是这个循环里面类似Append节点的操作,在读取完第一个subplans节点中的元组后,会依次读取后续subplan中的元组,直到全部读取完毕。我们以前说过postgres是一次读取一个元组并处理一个元组的。这里也不例外,每读取一个元组后根据操作的类型分别调用ExecInsert/ExecUpdate/ExecDelete函数去处理。

  • (3)有始有终,既然可能有BEFORE STATEMENT triggers,那么也可能有AFTER STATEMENT triggers,这里调用fireASTriggers函数来处理它。

  • 那么我们应该对ExecInsert/ExecUpdate/ExecDelete函数感兴趣了。下面我们开始讨论他们。

    1.ExecInsert

    对于ExecInsert函数的话,主要是两件事:将元组插入到目标表target relation中同时将对应的索引项插入到相关的索引表index relations(可能有多个索引要处理)中。

  • (1)首先要将需要插入的tuple从slot中取出,本地化。why?因为这个slot在随后的操作heap_insert函数中可能不安全,因此将其提前取出来。这个工作由ExecMaterializeSlot函数完成。

  • (2)从全局状态中estate->es_result_relation_info获取信息,判断result relation是否需要一个oid。如果需要,则先将tuple中的oid字段设为0。

  • (3)处理BEFORE ROW INSERT Triggers。这里我们要注意这个触发器是ROW级别的,而BEFORE STATEMENT triggers是语句级别的,他们不一样。

  • (4)处理INSTEAD OF ROW INSERT Triggers。如果存在则调用ExecIRInsertTriggers函数去处理并直接返回,不进行INSERT操作。

  • (5)处理foreign table的情况,为其初始化ri_FdwRoutine。调用foreign server的API去处理该条元组的插入并获取返回的slot

  • (6)处理WITH CHECK OPTION中的条件(ExecWithCheckOptions函数)和唯一性约束(ExecConstraints函数)ON ONCONFLICT OPTION。

  • (7)如果存在ON ONCONFLICT OPTION条件,则先获得speculative insertion lock,调用heap_insert函数将元组插入到堆表中。如果插入成功,不发生冲突则正常释放该lock。否则强制释放lock,并执行ON ONCONFLICT DO UPDATE(如果有的话)。

  • (8)不存在(7)中的条件,我们正常地调用heap_insert函数将元组插入到堆表中。同时调用ExecInsertIndexTuples函数插入相应的索引元组。

  • (9)调用ExecARInsertTriggers函数处理AFTER ROW INSERT Triggers。类似(3)的处理。

  • (10)还记得上面提到的CREATE VIEW中的WITH CHECK OPTION么?这里调用ExecWithCheckOptions函数做处理,不满足则报错退出。

  • (11)如果存在RETURNING子句,我们调用ExecProcessReturning函数处理之。

  • 2.ExecDelete

    ExecDelete函数相对简单,他只需要将元组删除即可,不需要针对索引做任何操作。

  • (1)从全局状态中estate->es_result_relation_info获取信息。

  • (2)处理BEFORE ROW DELETE Triggers。这里我们要注意这个触发器是ROW级别的,而BEFORE STATEMENT triggers是语句级别的,他们不一样。

  • (3)处理INSTEAD OF ROW DELETE Triggers。如果存在则调用ExecIRDeleteTriggers函数去处理并直接返回,不进行INSERT操作。

  • (4)处理foreign table的情况,为其初始化ri_FdwRoutine。调用foreign server的API去处理该条元组的删除并获取返回的slot。

  • (5)我们正常地调用heap_delete函数执行DELETE操作。如果返回值不是HeapTupleMayBeUpdated则说明操作失败,根据失败的错误代码执行相应的处理。

  • (6)调用ExecARDeleteTriggers函数处理AFTER ROW DELETE Triggers。类似(2)的处理。

  • (7)如果存在RETURNING子句,我们调用ExecProcessReturning函数处理之。

  • 3.ExecUpdate

    ExecUpdate函数实际上执行的是"INSERT"操作。因为postgres内部是MVCC机制,多版本并发控制。旧的元组实际上没有删除,只是不再引用。同时,UPDATE操作在数据库内部也是要在"transaction"中的,否则postgres会不停的将新增的updated元组看成是需要update的元组,循环下去。

  • (1)判断当前是否属于BootstrapProcessing模式,在该模式下所有的transaction id都被设置为1。这个时候才能保证不循环更新。

  • (2)首先要将需要插入的tuple从slot中取出,本地化。why?因为这个slot在随后的操作heap_update函数中可能不安全,因此将其提前取出来。这个工作由ExecMaterializeSlot函数完成。

  • (3)处理BEFORE ROW UPDATE Triggers。

  • (4)处理INSTEAD OF ROW UPDATE Triggers。如果存在则调用ExecIRUpdateTriggers函数去处理并直接返回,不进行INSERT操作。

  • (5)处理foreign table的情况,为其初始化ri_FdwRoutine。调用foreign server的API去处理该条元组的更新并获取返回的slot。

  • (6)处理WITH CHECK OPTION中的条件(ExecWithCheckOptions函数)和唯一性约束(ExecConstraints函数)ON ONCONFLICT OPTION。

  • (7)我们正常地调用heap_update函数执行UPDATE、操作。如果返回值不是HeapTupleMayBeUpdated则说明操作失败,根据失败的错误代码执行相应的处理。如果成功,则调用ExecInsertIndexTuples函数向索引中插入索引元组。

  • (8)调用ExecARUpdateTriggers函数处理AFTER ROW UPDATE Triggers。

  • (9)针对表的上层VIEW再次执行WITH CHECK OPTION。

  • (10)如果存在RETURNING子句,我们调用ExecProcessReturning函数处理之。



  • ModifyTable节点的清理简单些了(ExecEndModifyTable函数)。除了常规的清理工作,清理可能存在FDW结构,清理初始化中额外初始化的那些subplans节点。



    control节点到此结束。

    跟我一起读postgresql源码(十六)——Executor(查询执行模块之——control节点(下))

    标签:select   引用   node   text   pen   apply   gre   first   exec   

    热心网友 时间:2022-04-10 13:27

    we start to learn new words .please tell me loudly reading追答Tody .l'm glad to teach us new words .please reading after me

    热心网友 时间:2022-04-10 14:45

    I am glad to teach the Word for everyone,follow me please.

    热心网友 时间:2022-04-10 16:19

    Today I am very happy to teach you the words,please read after me.

    热心网友 时间:2022-04-10 18:11

     

    热心网友 时间:2022-04-10 20:19

    我来

    热心网友 时间:2022-04-10 22:43

    教我
    声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
    临沂比较有名的男装品牌 呼伦贝尔市悦动网络科技有限公司怎么样? 呼伦贝尔中汇实业有限公司怎么样? 呼伦贝尔油玉不绝电子商务有限公司怎么样? 如何避免wps卡顿? 属鼠的男人找对象是属什么,属鼠的人和什么属相合 96年鼠的姻缘在哪年 属相相合年份运势提升 2024属鼠找对象属什么最佳 黑客攻击网站能报案吗 黑客攻击报案有用吗 为什么焦俊艳就是火不起来? 《中国机长》请了朱亚文吴樾等11位明星来客串,为何只狂捧他? 如何评价《陆垚知马俐》中的焦俊艳? 朱亚文演的让叛逆少女变成贤妻良母的电视剧叫什么? 朱亚文和焦俊艳床戏的电影是什么电影 朱亚文强吻焦俊艳 哪一集? 《陆垚知马俐》朱亚文和焦俊艳是什么关系? 华为c8812手机先打开通讯录—更多—会出现导入/导出—导出在哪里 什么谁也什么谁也什么造句? 什么在什么在什么在什么造句二年级 小学二年级四字成语造句。 这么造句行吗? 宽广的天空 飘着美丽的气球?这样造句行吗? 小学二年级 请什么什么行吗?造句 (我仔细地看书)(我仔细地写作业)(我仔细地读书)二年级这样造句行吗? 行造句二年级, 用“行吗”造句 360安全模式手机如何开启 三洋,43ce660 LED电视上边很多条杠,不是摔的,还有维修的价值吗,维修的话修哪里_百度问一问 三洋电视机怎么样 三洋电视维修点总结 焦俊艳实力很强,为什么在娱乐圈一直不火? 朱亚文演的赵奔是什么电影 求漂洋过海来看你 朱亚文版的 焦俊艳强吻高以翔怎么回事 快乐大本营前几期节目的一个组合!记得看见朱亚文!快! 《中国机长》新海报李现*帅,阚清子打扮美,焦俊艳为什么造型一般? 方灰灰陆垚什么电视剧 朱亚文和刘亦菲是什么关系? ()应龙蛇是什么成语 怎么应付蛇 如何应对蛇缠腰呢? 喜力啤酒广东产的为什么是小麦做的,不是应该蛇麻子吗 有没有野外实用生存的相关东西呢? 毒蛇有什么药用价值? 巳蛇 是什么? 香椿树老枝可以水培扦插吗 有能让香椿树长矮长粗的方法吗? 广东水里的菜什么长得最快 els-an00多少钱 请教如何使用Swift获取系统通讯录信息