摘要:
postgres的语句执行可以分为客户端连接层, 查询分析层, 执行计划层,存储层。
本文记录insert语句在源码中如何执行的。
流程概述:
执行流程:
这里我们将介绍为了获取结果,一个查询将会经历那些阶段。
1. 一个由应用程序到PostgreSQL服务器的连接必须被建立。应用程序传递一个查询给服务
器并等待接收由服务器传回的结果。
2. 分析阶段对由应用程序传递的查询进行语法检查,并创建一个查询树。
3. 重写系统得到分析阶段创建的查询树,并查找可以应用到该查询树的任何规则(存储
在系统目录中)。对找到的规则,它会执行规则体中给定的转换。
重写系统的一个应用是实现视图。不论何时发生一个视图(即一个虚拟表)上的查询,
重写系统将用户查询重写为一个访问视图定义中给定的基本表的查询来替代。
4. 规划器/优化器接手(重写过的)查询树并创建一个将被作为执行器输入的查询计划。
它会先创建所有可能导向相同结果的路径。例如,如果在一个被扫描的关系上有一个索
引,则有两条可供扫描的路径。其中之一是一个简单的顺序扫描,而另一个则是使用索
引。接下来执行每条路径所需的代价被估算出来并且代价最低的路径将被选中。代价最
低的路径将被扩展成一个完整的计划可供执行器使用。
5. 执行器递归地逐步通过计划树并按照计划表述的方式获取行。执行器在扫描关系时会使
用存储系统、执行排序和连接、估算条件并最后归还得到的行。
执行器:
执行器接手规划器/优化器创建的计划,并递归地处理之以抽取所需的行集。这本质上是一
种需求拉动的管道机制。每次一个计划节点被调用时,它必须交付一个或多个行,或者报告
已经完成了行的交付。
为了提供一个具体例子,假设顶层节点是一个MergeJoin节点。在归并完成之前,两个行必
须先被获取(每一个来自于一个子计划)。因此执行器递归地调用它自己去处理子计划(从
附加在lefttree的子计划开始)。新的顶层节点(左子计划的顶层节点),我们说是一
个Sort节点,并且又要递归来获取一个输入行。Sort的子节点可以是一个SeqScan节点,表
示真正地读取一个表。该节点的执行将会使执行器从表中获取一行并将它返回给调用节
点。Sort节点将反复调用它的子节点来获得所有需要排序的行。当输入耗尽后(子节点将返
回一个NULL来标识),Sort节点执行排序,并且最后能够返回它的第一个输出行,及排序后
的第一个。它会把剩下的行保存下来,这样它可以根据后续的要求按照排好的顺序返回这些
行。
MergeJoin节点也会相似地从其右子计划要求第一个行。然后它会比较两个子节点提供的行
看它们是否能被连接,如果可以它会返回一个连接行给调用者。在下一次调用时,或者它无
法连接当前的输入对时,它会前进到一个表或另一个表的下一行(取决于比较的结果),并
再次检查匹配。最后,某个子计划耗尽,MergeJoin节点返回NULL表示它没有更多连接行可
以提供。
复杂的查询可能涉及多层计划节点,但是一般的方法是相同的:每个节点在被调用时计算并
返回它的下一个输出行。每个节点同时也负责应用由规划器分配给它的选择或投影表达式。
执行器机制被用于四种基本SQL查询类型:SELECT、INSERT、
UPDATE以及DELETE。对
于SELECT,顶层执行器代码只需要发送查询计划树返回的每个行给客户端。对于INSERT,每
一个被返回的行被插入到INSERT中指定的目标表中。这通过一个被称为ModifyTable的特殊
顶层计划节点完成(一个简单的INSERT ... VALUES命令会创建一个由一个Result节点组成的
简单计划树,该节点只计算一个结果行,在它之上的ModifyTable节点会执行插入。但
是INSERT ... SELECT可以用到执行器机制的全部功能)。对于UPDATE,规划器会安排每一个
计算行包含所有被更新的列值加上原始目标行的TID(元组ID或行ID),这些数据也会被输
入到一个ModifyTable节点,该节点将利用这些信息创建一个新的被更新行并标记旧行为被
删除。对于DELETE,唯一被计划返回的列是TID,ModifyTable节点简单地使用TID访问每一
个目标行并将其标记为被删除。
tuple:
核心流程:
(gdb) bt
#0 ExecInsert (mtstate=0x2087a38, resultRelInfo=0x2087c48, slot=0x2088f78, planSlot=0x20884e8, estate=0x20877d8, canSetTag=true) at nodeModifyTable.c:602
#1 0x000000000072498e in ExecModifyTable (pstate=0x2087a38) at nodeModifyTable.c:2561
#2 0x00000000006edb08 in ExecProcNodeFirst (node=0x2087a38) at execProcnode.c:463
#3 0x00000000006e3279 in ExecProcNode (node=0x2087a38) at ../../../src/include/executor/executor.h:257
#4 0x00000000006e56a9 in ExecutePlan (estate=0x20877d8, planstate=0x2087a38, use_parallel_mode=false, operation=CMD_INSERT, sendTuples=false, numberTuples=0,
direction=ForwardScanDirection, dest=0x206da58, execute_once=true) at execMain.c:1551
#5 0x00000000006e37b7 in standard_ExecutorRun (queryDesc=0x20c5048, direction=ForwardScanDirection, count=0, execute_once=true) at execMain.c:361
#6 0x00000000006e3654 in ExecutorRun (queryDesc=0x20c5048, direction=ForwardScanDirection, count=0, execute_once=true) at execMain.c:305
#7 0x00000000008fabfd in ProcessQuery (plan=0x206d978,
sourceText=0x1fb2a28 "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY,JOIN_DATE) VALUES (17, 'Paul', 32, 'California', 20000.00,'2001-07-13');", params=0x0, queryEnv=0x0,
dest=0x206da58, qc=0x7ffcf6b62bb0) at pquery.c:160
#8 0x00000000008fc34a in PortalRunMulti (portal=0x2015898, isTopLevel=true, setHoldSnapshot=false, dest=0x206da58, altdest=0x206da58, qc=0x7ffcf6b62bb0) at pquery.c:1274
#9 0x00000000008fb9ac in PortalRun (portal=0x2015898, count=9223372036854775807, isTopLevel=true, run_once=true, dest=0x206da58, altdest=0x206da58, qc=0x7ffcf6b62bb0) at pquery.c:788
#10 0x00000000008f5a77 in exec_simple_query (
query_string=0x1fb2a28 "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY,JOIN_DATE) VALUES (17, 'Paul', 32, 'California', 20000.00,'2001-07-13');") at postgres.c:1214
#11 0x00000000008f9d66 in PostgresMain (argc=1, argv=0x7ffcf6b62e40, dbname=0x1fdf898 "test", username=0x1fadff8 "kevin") at postgres.c:4496
#12 0x000000000084abfe in BackendRun (port=0x1fd6ed0) at postmaster.c:4530
#13 0x000000000084a57a in BackendStartup (port=0x1fd6ed0) at postmaster.c:4252
#14 0x0000000000846d3b in ServerLoop () at postmaster.c:1745
#15 0x0000000000846612 in PostmasterMain (argc=1, argv=0x1fabfb0) at postmaster.c:1417
#16 0x0000000000756f66 in main (argc=1, argv=0x1fabfb0) at main.c:209
(gdb) p *planSlot
$37 = {
type = T_TupleTableSlot,
tts_flags = 16,
tts_nvalid = 6,
tts_ops = 0xbcee80 ,
tts_tupleDescriptor = 0x20880d8,
tts_values = 0x2088530,
tts_isnull = 0x2088560,
tts_mcxt = 0x20876c0,
tts_tid = {
ip_blkid = {
bi_hi = 65535,
bi_lo = 65535
},
ip_posid = 0
},
tts_tableOid = 0
}
(gdb) p *resultRelInfo
$39 = {
type = T_ResultRelInfo,
ri_RangeTableIndex = 1,
ri_RelationDesc = 0x7f849a8415a8,
ri_NumIndices = 0,
ri_IndexRelationDescs = 0x0,
ri_IndexRelationInfo = 0x0,
ri_RowIdAttNo = 0,
ri_projectNew = 0x0,
ri_newTupleSlot = 0x0,
ri_oldTupleSlot = 0x0,
ri_projectNewInfoValid = false,
ri_TrigDesc = 0x0,
ri_TrigFunctions = 0x0,
ri_TrigWhenExprs = 0x0,
ri_TrigInstrument = 0x0,
ri_ReturningSlot = 0x0,
ri_TrigOldSlot = 0x0,
ri_TrigNewSlot = 0x0,
ri_FdwRoutine = 0x0,
ri_FdwState = 0x0,
ri_usesFdwDirectModify = false,
ri_NumSlots = 0,
ri_NumSlotsInitialized = 0,
ri_BatchSize = 1,
ri_Slots = 0x0,
ri_PlanSlots = 0x0,
ri_WithCheckOptions = 0x0,
ri_WithCheckOptionExprs = 0x0,
ri_ConstraintExprs = 0x0,
ri_GeneratedExprs = 0x0,
ri_NumGeneratedNeeded = 0,
ri_returningList = 0x0,
ri_projectReturning = 0x0,
ri_onConflictArbiterIndexes = 0x0,
ri_onConflict = 0x0,
ri_PartitionCheckExpr = 0x0,
ri_RootResultRelInfo = 0x0,
ri_RootToPartitionMap = 0x0,
ri_PartitionTupleSlot = 0x0,
ri_ChildToRootMap = 0x0,
ri_ChildToRootMapValid = false,
ri_CopyMultiInsertBuffer = 0x0
}
(gdb) p *slot
$43 = {
type = 4139132976,
tts_flags = 32764,
tts_nvalid = 0,
tts_ops = 0x6edb08 ,
tts_tupleDescriptor = 0x110,
tts_values = 0x2087a38,
tts_isnull = 0x7ffcf6b62850,
tts_mcxt = 0x6e3279 ,
tts_tid = {
ip_blkid = {
bi_hi = 24,
bi_lo = 0
},
ip_posid = 0
},
tts_tableOid = 34110008
}
(gdb) p *resultRelInfo
$47 = {
type = T_ResultRelInfo,
ri_RangeTableIndex = 1,
ri_RelationDesc = 0x7f849a8415a8,
ri_NumIndices = 0,
ri_IndexRelationDescs = 0x0,
ri_IndexRelationInfo = 0x0,
ri_RowIdAttNo = 0,
ri_projectNew = 0x0,
ri_newTupleSlot = 0x0,
ri_oldTupleSlot = 0x0,
ri_projectNewInfoValid = false,
ri_TrigDesc = 0x0,
ri_TrigFunctions = 0x0,
ri_TrigWhenExprs = 0x0,
ri_TrigInstrument = 0x0,
ri_ReturningSlot = 0x0,
ri_TrigOldSlot = 0x0,
ri_TrigNewSlot = 0x0,
ri_FdwRoutine = 0x0,
ri_FdwState = 0x0,
ri_usesFdwDirectModify = false,
ri_NumSlots = 0,
ri_NumSlotsInitialized = 0,
ri_BatchSize = 1,
ri_Slots = 0x0,
ri_PlanSlots = 0x0,
ri_WithCheckOptions = 0x0,
ri_WithCheckOptionExprs = 0x0,
ri_ConstraintExprs = 0x0,
ri_GeneratedExprs = 0x0,
ri_NumGeneratedNeeded = 0,
ri_returningList = 0x0,
ri_projectReturning = 0x0,
ri_onConflictArbiterIndexes = 0x0,
ri_onConflict = 0x0,
ri_PartitionCheckExpr = 0x0,
ri_RootResultRelInfo = 0x0,
ri_RootToPartitionMap = 0x0,
ri_PartitionTupleSlot = 0x0,
ri_ChildToRootMap = 0x0,
ri_ChildToRootMapValid = false,
ri_CopyMultiInsertBuffer = 0x0
}
(gdb) p *node
$48 = {
ps = {
type = T_ModifyTableState,
plan = 0x206d4e8,
state = 0x20877d8,
ExecProcNode = 0x7244b5 ,
ExecProcNodeReal = 0x7244b5 ,
instrument = 0x0,
worker_instrument = 0x0,
worker_jit_instrument = 0x0,
qual = 0x0,
lefttree = 0x2087ee8,
righttree = 0x0,
initPlan = 0x0,
subPlan = 0x0,
chgParam = 0x0,
ps_ResultTupleDesc = 0x2088e68,
ps_ResultTupleSlot = 0x0,
ps_ExprContext = 0x0,
ps_ProjInfo = 0x0,
async_capable = false,
scandesc = 0x0,
scanops = 0x0,
outerops = 0x0,
innerops = 0x0,
resultops = 0x0,
scanopsfixed = false,
outeropsfixed = false,
inneropsfixed = false,
resultopsfixed = false,
scanopsset = false,
outeropsset = false,
inneropsset = false,
resultopsset = false
},
operation = CMD_INSERT,
canSetTag = true,
mt_done = false,
mt_nrels = 1,
resultRelInfo = 0x2087c48,
rootResultRelInfo = 0x2087c48,
mt_epqstate = {
parentestate = 0x20877d8,
epqParam = 0,
tuple_table = 0x0,
relsubs_slot = 0x2087ec8,
--Type for more, q to quit, c to continue without paging--
plan = 0x205c940,
arowMarks = 0x0,
origslot = 0x0,
recheckestate = 0x0,
relsubs_rowmark = 0x0,
relsubs_done = 0x0,
recheckplanstate = 0x0
},
fireBSTriggers = false,
mt_resultOidAttno = 0,
mt_lastResultOid = 0,
mt_lastResultIndex = 0,
mt_resultOidHash = 0x0,
mt_root_tuple_slot = 0x0,
mt_partition_tuple_routing = 0x0,
mt_transition_capture = 0x0,
mt_oc_transition_capture = 0x0
}
核心函数:
ExecInsert
static TupleTableSlot *
ExecInsert(ModifyTableState *mtstate,
ResultRelInfo *resultRelInfo,
TupleTableSlot *slot,
TupleTableSlot *planSlot,
EState *estate,
bool canSetTag)
{
Relation resultRelationDesc;
List *recheckIndexes = NIL;
TupleTableSlot *result = NULL;
TransitionCaptureState *ar_insert_trig_tcs;
ModifyTable *node = (ModifyTable *) mtstate->ps.plan;
OnConflictAction onconflict = node->onConflictAction;
PartitionTupleRouting *proute = mtstate->mt_partition_tuple_routing;
MemoryContext oldContext;
if (proute)
{
ResultRelInfo *partRelInfo;
slot = ExecPrepareTupleRouting(mtstate, estate, proute,
resultRelInfo, slot,
&partRelInfo);
resultRelInfo = partRelInfo;
}
ExecMaterializeSlot(slot);
resultRelationDesc = resultRelInfo->ri_RelationDesc;
if (resultRelationDesc->rd_rel->relhasindex &&
resultRelInfo->ri_IndexRelationDescs == NULL)
ExecOpenIndices(resultRelInfo, onconflict != ONCONFLICT_NONE);
if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_insert_before_row)
{
if (!ExecBRInsertTriggers(estate, resultRelInfo, slot))
return NULL;
}
if (resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_insert_instead_row)
{
if (!ExecIRInsertTriggers(estate, resultRelInfo, slot))
return NULL;
}
else if (resultRelInfo->ri_FdwRoutine)
{
slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
if (resultRelationDesc->rd_att->constr &&
resultRelationDesc->rd_att->constr->has_generated_stored)
ExecComputeStoredGenerated(resultRelInfo, estate, slot,
CMD_INSERT);
if (resultRelInfo->ri_BatchSize > 1)
{
if (resultRelInfo->ri_NumSlots == resultRelInfo->ri_BatchSize)
{
ExecBatchInsert(mtstate, resultRelInfo,
resultRelInfo->ri_Slots,
resultRelInfo->ri_PlanSlots,
resultRelInfo->ri_NumSlots,
estate, canSetTag);
resultRelInfo->ri_NumSlots = 0;
}
oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
if (resultRelInfo->ri_Slots == NULL)
{
resultRelInfo->ri_Slots = palloc(sizeof(TupleTableSlot *) *
resultRelInfo->ri_BatchSize);
resultRelInfo->ri_PlanSlots = palloc(sizeof(TupleTableSlot *) *
resultRelInfo->ri_BatchSize);
}
if (resultRelInfo->ri_NumSlots >= resultRelInfo->ri_NumSlotsInitialized)
{
TupleDesc tdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
TupleDesc plan_tdesc =
CreateTupleDescCopy(planSlot->tts_tupleDescriptor);
resultRelInfo->ri_Slots[resultRelInfo->ri_NumSlots] =
MakeSingleTupleTableSlot(tdesc, slot->tts_ops);
resultRelInfo->ri_PlanSlots[resultRelInfo->ri_NumSlots] =
MakeSingleTupleTableSlot(plan_tdesc, planSlot->tts_ops);
resultRelInfo->ri_NumSlotsInitialized++;
}
ExecCopySlot(resultRelInfo->ri_Slots[resultRelInfo->ri_NumSlots],
slot);
ExecCopySlot(resultRelInfo->ri_PlanSlots[resultRelInfo->ri_NumSlots],
planSlot);
resultRelInfo->ri_NumSlots++;
MemoryContextSwitchTo(oldContext);
return NULL;
}
slot = resultRelInfo->ri_FdwRoutine->ExecForeignInsert(estate,
resultRelInfo,
slot,
planSlot);
if (slot == NULL)
return NULL;
slot->tts_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc);
}
else
{
WCOKind wco_kind;
slot->tts_tableOid = RelationGetRelid(resultRelationDesc);
if (resultRelationDesc->rd_att->constr &&
resultRelationDesc->rd_att->constr->has_generated_stored)
ExecComputeStoredGenerated(resultRelInfo, estate, slot,
CMD_INSERT);
wco_kind = (mtstate->operation == CMD_UPDATE) ?
WCO_RLS_UPDATE_CHECK : WCO_RLS_INSERT_CHECK;
if (resultRelInfo->ri_WithCheckOptions != NIL)
ExecWithCheckOptions(wco_kind, resultRelInfo, slot, estate);
if (resultRelationDesc->rd_att->constr)
ExecConstraints(resultRelInfo, slot, estate);
if (resultRelationDesc->rd_rel->relispartition &&
(resultRelInfo->ri_RootResultRelInfo == NULL ||
(resultRelInfo->ri_TrigDesc &&
resultRelInfo->ri_TrigDesc->trig_insert_before_row)))
ExecPartitionCheck(resultRelInfo, slot, estate, true);
if (onconflict != ONCONFLICT_NONE && resultRelInfo->ri_NumIndices > 0)
{
uint32 specToken;
ItemPointerData conflictTid;
bool specConflict;
List *arbiterIndexes;
arbiterIndexes = resultRelInfo->ri_onConflictArbiterIndexes;
vlock:
specConflict = false;
if (!ExecCheckIndexConstraints(resultRelInfo, slot, estate,
&conflictTid, arbiterIndexes))
{
if (onconflict == ONCONFLICT_UPDATE)
{
TupleTableSlot *returning = NULL;
if (ExecOnConflictUpdate(mtstate, resultRelInfo,
&conflictTid, planSlot, slot,
estate, canSetTag, &returning))
{
InstrCountTuples2(&mtstate->ps, 1);
return returning;
}
else
goto vlock;
}
else
{
Assert(onconflict == ONCONFLICT_NOTHING);
ExecCheckTIDVisible(estate, resultRelInfo, &conflictTid,
ExecGetReturningSlot(estate, resultRelInfo));
InstrCountTuples2(&mtstate->ps, 1);
return NULL;
}
}
specToken = SpeculativeInsertionLockAcquire(GetCurrentTransactionId());
table_tuple_insert_speculative(resultRelationDesc, slot,
estate->es_output_cid,
0,
NULL,
specToken);
recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
slot, estate, false, true,
&specConflict,
arbiterIndexes);
table_tuple_complete_speculative(resultRelationDesc, slot,
specToken, !specConflict);
SpeculativeInsertionLockRelease(GetCurrentTransactionId());
if (specConflict)
{
list_free(recheckIndexes);
goto vlock;
}
}
else
{
table_tuple_insert(resultRelationDesc, slot,
estate->es_output_cid,
0, NULL);
if (resultRelInfo->ri_NumIndices > 0)
recheckIndexes = ExecInsertIndexTuples(resultRelInfo,
slot, estate, false,
false, NULL, NIL);
}
}
if (canSetTag)
(estate->es_processed)++;
ar_insert_trig_tcs = mtstate->mt_transition_capture;
if (mtstate->operation == CMD_UPDATE && mtstate->mt_transition_capture
&& mtstate->mt_transition_capture->tcs_update_new_table)
{
ExecARUpdateTriggers(estate, resultRelInfo, NULL,
NULL,
slot,
NULL,
mtstate->mt_transition_capture);
ar_insert_trig_tcs = NULL;
}
ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes,
ar_insert_trig_tcs);
list_free(recheckIndexes);
if (resultRelInfo->ri_WithCheckOptions != NIL)
ExecWithCheckOptions(WCO_VIEW_CHECK, resultRelInfo, slot, estate);
if (resultRelInfo->ri_projectReturning)
result = ExecProcessReturning(resultRelInfo, slot, planSlot);
return result;
}
ExecModifyTable
static TupleTableSlot *
ExecModifyTable(PlanState *pstate)
{
ModifyTableState *node = castNode(ModifyTableState, pstate);
EState *estate = node->ps.state;
CmdType operation = node->operation;
ResultRelInfo *resultRelInfo;
PlanState *subplanstate;
TupleTableSlot *slot;
TupleTableSlot *planSlot;
TupleTableSlot *oldSlot;
ItemPointer tupleid;
ItemPointerData tuple_ctid;
HeapTupleData oldtupdata;
HeapTuple oldtuple;
PartitionTupleRouting *proute = node->mt_partition_tuple_routing;
List *relinfos = NIL;
ListCell *lc;
CHECK_FOR_INTERRUPTS();
if (estate->es_epq_active != NULL)
elog(ERROR, "ModifyTable should not be called during EvalPlanQual");
if (node->mt_done)
return NULL;
if (node->fireBSTriggers)
{
fireBSTriggers(node);
node->fireBSTriggers = false;
}
resultRelInfo = node->resultRelInfo + node->mt_lastResultIndex;
subplanstate = outerPlanState(node);
for (;;)
{
ResetPerTupleExprContext(estate);
if (pstate->ps_ExprContext)
ResetExprContext(pstate->ps_ExprContext);
planSlot = ExecProcNode(subplanstate);
if (TupIsNull(planSlot))
break;
if (AttributeNumberIsValid(node->mt_resultOidAttno))
{
Datum datum;
bool isNull;
Oid resultoid;
datum = ExecGetJunkAttribute(planSlot, node->mt_resultOidAttno,
&isNull);
if (isNull)
elog(ERROR, "tableoid is NULL");
resultoid = DatumGetObjectId(datum);
if (resultoid != node->mt_lastResultOid)
resultRelInfo = ExecLookupResultRelByOid(node, resultoid,
false, true);
}
if (resultRelInfo->ri_usesFdwDirectModify)
{
Assert(resultRelInfo->ri_projectReturning);
slot = ExecProcessReturning(resultRelInfo, NULL, planSlot);
return slot;
}
EvalPlanQualSetSlot(&node->mt_epqstate, planSlot);
slot = planSlot;
tupleid = NULL;
oldtuple = NULL;
if (operation == CMD_UPDATE || operation == CMD_DELETE)
{
char relkind;
Datum datum;
bool isNull;
relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
if (relkind == RELKIND_RELATION ||
relkind == RELKIND_MATVIEW ||
relkind == RELKIND_PARTITIONED_TABLE)
{
Assert(AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo));
datum = ExecGetJunkAttribute(slot,
resultRelInfo->ri_RowIdAttNo,
&isNull);
if (isNull)
elog(ERROR, "ctid is NULL");
tupleid = (ItemPointer) DatumGetPointer(datum);
tuple_ctid = *tupleid;
tupleid = &tuple_ctid;
}
else if (AttributeNumberIsValid(resultRelInfo->ri_RowIdAttNo))
{
datum = ExecGetJunkAttribute(slot,
resultRelInfo->ri_RowIdAttNo,
&isNull);
if (isNull)
elog(ERROR, "wholerow is NULL");
oldtupdata.t_data = DatumGetHeapTupleHeader(datum);
oldtupdata.t_len =
HeapTupleHeaderGetDatumLength(oldtupdata.t_data);
ItemPointerSetInvalid(&(oldtupdata.t_self));
oldtupdata.t_tableOid =
(relkind == RELKIND_VIEW) ? InvalidOid :
RelationGetRelid(resultRelInfo->ri_RelationDesc);
oldtuple = &oldtupdata;
}
else
{
Assert(relkind == RELKIND_FOREIGN_TABLE);
}
}
switch (operation)
{
case CMD_INSERT:
if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
ExecInitInsertProjection(node, resultRelInfo);
slot = ExecGetInsertNewTuple(resultRelInfo, planSlot);
slot = ExecInsert(node, resultRelInfo, slot, planSlot,
estate, node->canSetTag);
break;
case CMD_UPDATE:
if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
ExecInitUpdateProjection(node, resultRelInfo);
oldSlot = resultRelInfo->ri_oldTupleSlot;
if (oldtuple != NULL)
{
ExecForceStoreHeapTuple(oldtuple, oldSlot, false);
}
else
{
Relation relation = resultRelInfo->ri_RelationDesc;
Assert(tupleid != NULL);
if (!table_tuple_fetch_row_version(relation, tupleid,
SnapshotAny,
oldSlot))
elog(ERROR, "failed to fetch tuple being updated");
}
slot = ExecGetUpdateNewTuple(resultRelInfo, planSlot,
oldSlot);
slot = ExecUpdate(node, resultRelInfo, tupleid, oldtuple, slot,
planSlot, &node->mt_epqstate, estate,
node->canSetTag);
break;
case CMD_DELETE:
slot = ExecDelete(node, resultRelInfo, tupleid, oldtuple,
planSlot, &node->mt_epqstate, estate,
true,
node->canSetTag,
false,
NULL, NULL);
break;
default:
elog(ERROR, "unknown operation");
break;
}
if (slot)
return slot;
}
if (proute)
relinfos = estate->es_tuple_routing_result_relations;
else
relinfos = estate->es_opened_result_relations;
foreach(lc, relinfos)
{
resultRelInfo = lfirst(lc);
if (resultRelInfo->ri_NumSlots > 0)
ExecBatchInsert(node, resultRelInfo,
resultRelInfo->ri_Slots,
resultRelInfo->ri_PlanSlots,
resultRelInfo->ri_NumSlots,
estate, node->canSetTag);
}
fireASTriggers(node);
node->mt_done = true;
return NULL;
}
exec_simple_query
static void
exec_simple_query(const char *query_string)
{
CommandDest dest = whereToSendOutput;
MemoryContext oldcontext;
List *parsetree_list;
ListCell *parsetree_item;
bool save_log_statement_stats = log_statement_stats;
bool was_logged = false;
bool use_implicit_block;
char msec_str[32];
debug_query_string = query_string;
pgstat_report_activity(STATE_RUNNING, query_string);
TRACE_POSTGRESQL_QUERY_START(query_string);
if (save_log_statement_stats)
ResetUsage();
start_xact_command();
drop_unnamed_stmt();
oldcontext = MemoryContextSwitchTo(MessageContext);
parsetree_list = pg_parse_query(query_string);
if (check_log_statement(parsetree_list))
{
ereport(LOG,
(errmsg("statement: %s", query_string),
errhidestmt(true),
errdetail_execute(parsetree_list)));
was_logged = true;
}
MemoryContextSwitchTo(oldcontext);
use_implicit_block = (list_length(parsetree_list) > 1);
foreach(parsetree_item, parsetree_list)
{
RawStmt *parsetree = lfirst_node(RawStmt, parsetree_item);
bool snapshot_set = false;
CommandTag commandTag;
QueryCompletion qc;
MemoryContext per_parsetree_context = NULL;
List *querytree_list,
*plantree_list;
Portal portal;
DestReceiver *receiver;
int16 format;
pgstat_report_query_id(0, true);
commandTag = CreateCommandTag(parsetree->stmt);
set_ps_display(GetCommandTagName(commandTag));
BeginCommand(commandTag, dest);
if (IsAbortedTransactionBlockState() &&
!IsTransactionExitStmt(parsetree->stmt))
ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, "
"commands ignored until end of transaction block"),
errdetail_abort()));
start_xact_command();
if (use_implicit_block)
BeginImplicitTransactionBlock();
CHECK_FOR_INTERRUPTS();
if (analyze_requires_snapshot(parsetree))
{
PushActiveSnapshot(GetTransactionSnapshot());
snapshot_set = true;
}
if (lnext(parsetree_list, parsetree_item) != NULL)
{
per_parsetree_context =
AllocSetContextCreate(MessageContext,
"per-parsetree message context",
ALLOCSET_DEFAULT_SIZES);
oldcontext = MemoryContextSwitchTo(per_parsetree_context);
}
else
oldcontext = MemoryContextSwitchTo(MessageContext);
querytree_list = pg_analyze_and_rewrite(parsetree, query_string,
NULL, 0, NULL);
plantree_list = pg_plan_queries(querytree_list, query_string,
CURSOR_OPT_PARALLEL_OK, NULL);
if (snapshot_set)
PopActiveSnapshot();
CHECK_FOR_INTERRUPTS();
portal = CreatePortal("", true, true);
portal->visible = false;
PortalDefineQuery(portal,
NULL,
query_string,
commandTag,
plantree_list,
NULL);
PortalStart(portal, NULL, 0, InvalidSnapshot);
format = 0;
if (IsA(parsetree->stmt, FetchStmt))
{
FetchStmt *stmt = (FetchStmt *) parsetree->stmt;
if (!stmt->ismove)
{
Portal fportal = GetPortalByName(stmt->portalname);
if (PortalIsValid(fportal) &&
(fportal->cursorOptions & CURSOR_OPT_BINARY))
format = 1;
}
}
PortalSetResultFormat(portal, 1, &format);
receiver = CreateDestReceiver(dest);
if (dest == DestRemote)
SetRemoteDestReceiverParams(receiver, portal);
MemoryContextSwitchTo(oldcontext);
(void) PortalRun(portal,
FETCH_ALL,
true,
true,
receiver,
receiver,
&qc);
receiver->rDestroy(receiver);
PortalDrop(portal, false);
if (lnext(parsetree_list, parsetree_item) == NULL)
{
if (use_implicit_block)
EndImplicitTransactionBlock();
finish_xact_command();
}
else if (IsA(parsetree->stmt, TransactionStmt))
{
finish_xact_command();
}
else
{
CommandCounterIncrement();
disable_statement_timeout();
}
EndCommand(&qc, dest, false);
if (per_parsetree_context)
MemoryContextDelete(per_parsetree_context);
}
finish_xact_command();
if (!parsetree_list)
NullCommand(dest);
switch (check_log_duration(msec_str, was_logged))
{
case 1:
ereport(LOG,
(errmsg("duration: %s ms", msec_str),
errhidestmt(true)));
break;
case 2:
ereport(LOG,
(errmsg("duration: %s ms statement: %s",
msec_str, query_string),
errhidestmt(true),
errdetail_execute(parsetree_list)));
break;
}
if (save_log_statement_stats)
ShowUsage("QUERY STATISTICS");
TRACE_POSTGRESQL_QUERY_DONE(query_string);
debug_query_string = NULL;
}
ExecProcNode
#ifndef FRONTEND
static inline TupleTableSlot *
ExecProcNode(PlanState *node)
{
if (node->chgParam != NULL)
ExecReScan(node);
return node->ExecProcNode(node);
}
#endif
raw_parser
#ifndef FRONTEND
static inline TupleTableSlot *
ExecProcNode(PlanState *node)
{
if (node->chgParam != NULL)
ExecReScan(node);
return node->ExecProcNode(node);
}
#endif
List *
raw_parser(const char *str, RawParseMode mode)
{
core_yyscan_t yyscanner;
base_yy_extra_type yyextra;
int yyresult;
yyscanner = scanner_init(str, &yyextra.core_yy_extra,
&ScanKeywords, ScanKeywordTokens);
if (mode == RAW_PARSE_DEFAULT)
yyextra.have_lookahead = false;
else
{
static const int mode_token[] = {
0,
MODE_TYPE_NAME,
MODE_PLPGSQL_EXPR,
MODE_PLPGSQL_ASSIGN1,
MODE_PLPGSQL_ASSIGN2,
MODE_PLPGSQL_ASSIGN3
};
yyextra.have_lookahead = true;
yyextra.lookahead_token = mode_token[mode];
yyextra.lookahead_yylloc = 0;
yyextra.lookahead_end = NULL;
}
parser_init(&yyextra);
yyresult = base_yyparse(yyscanner);
scanner_finish(yyscanner);
if (yyresult)
return NIL;
return yyextra.parsetree;
}
pg_parse_query
List *
pg_parse_query(const char *query_string)
{
List *raw_parsetree_list;
TRACE_POSTGRESQL_QUERY_PARSE_START(query_string);
if (log_parser_stats)
ResetUsage();
raw_parsetree_list = raw_parser(query_string, RAW_PARSE_DEFAULT);
if (log_parser_stats)
ShowUsage("PARSER STATISTICS");
#ifdef COPY_PARSE_PLAN_TREES
{
List *new_list = copyObject(raw_parsetree_list);
if (!equal(new_list, raw_parsetree_list))
elog(WARNING, "copyObject() failed to produce an equal raw parse tree");
else
raw_parsetree_list = new_list;
}
#endif
TRACE_POSTGRESQL_QUERY_PARSE_DONE(query_string);
return raw_parsetree_list;
}



