Update execCurrent.cpp
This commit is contained in:
parent
50230625f0
commit
44f750e018
|
|
@ -1,13 +1,13 @@
|
|||
/* -------------------------------------------------------------------------
|
||||
*
|
||||
* execCurrent.c
|
||||
* executor support for WHERE CURRENT OF cursor
|
||||
* 执行程序支持WHERE CURRENT OF游标执行程序支持WHERE CURRENT OF游标
|
||||
*
|
||||
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
||||
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
* 华为技术有限公司版权所有
|
||||
* 部分版权所有(c) 1996-2012, PostgreSQL全球发展集团
|
||||
* 版权所有(c) 1994,加州大学董事会
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* 识别
|
||||
* src/backend/executor/execCurrent.c
|
||||
*
|
||||
* -------------------------------------------------------------------------
|
||||
|
|
@ -38,14 +38,12 @@ static ScanState* search_plan_tree(PlanState *node, Oid table_oid);
|
|||
/*
|
||||
* execCurrentOf
|
||||
*
|
||||
* Given a CURRENT OF expression and the OID of a table, determine which row
|
||||
* of the table is currently being scanned by the cursor named by CURRENT OF,
|
||||
* and return the row's TID into *current_tid.
|
||||
* 给定CURRENT OF表达式和表的OID,确定哪一行
|
||||
* 当前正在被名为CURRENT of的游标扫描
|
||||
* 并返回该行的TID为*current_tid。
|
||||
*
|
||||
* Returns TRUE if a row was identified. Returns FALSE if the cursor is valid
|
||||
* for the table but is not currently scanning a row of the table (this is a
|
||||
* legal situation in inheritance cases). Raises error if cursor is not a
|
||||
* valid updatable scan of the specified table.
|
||||
* 如果一行被识别,则返回TRUE。如果游标有效,则返回FALSE
|
||||
* 但是当前没有扫描表的一行(这是继承情况下的合法情况)。如果游标不是指定表的有效可更新扫描,则引发错误。
|
||||
*/
|
||||
bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relation, ItemPointer current_tid,
|
||||
RelationPtr partitionOfCursor_tid)
|
||||
|
|
@ -55,14 +53,14 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
|
|||
QueryDesc *query_desc = NULL;
|
||||
Oid table_oid = RelationGetRelid(relation);
|
||||
|
||||
/* Get the cursor name --- may have to look up a parameter reference */
|
||||
/* 获取游标名称——可能需要查找参数引用 */
|
||||
if (cexpr->cursor_name) {
|
||||
cursor_name = cexpr->cursor_name;
|
||||
} else {
|
||||
cursor_name = fetch_cursor_param_value(econtext, cexpr->cursor_param);
|
||||
}
|
||||
|
||||
/* Find the cursor's portal */
|
||||
/* 找到游标的入口 */
|
||||
portal = GetPortalByName(cursor_name);
|
||||
if (!PortalIsValid(portal)) {
|
||||
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_CURSOR),
|
||||
|
|
@ -70,8 +68,7 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
|
|||
}
|
||||
|
||||
/*
|
||||
* We have to watch out for non-SELECT queries as well as held cursors,
|
||||
* both of which may have null query_desc.
|
||||
* 我们必须注意非select查询和持有的游标,它们的query_desc都可能为空
|
||||
*/
|
||||
if (portal->strategy != PORTAL_ONE_SELECT) {
|
||||
ereport(ERROR,
|
||||
|
|
@ -85,26 +82,23 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
|
|||
}
|
||||
|
||||
/*
|
||||
* We have two different strategies depending on whether the cursor uses
|
||||
* FOR UPDATE/SHARE or not. The reason for supporting both is that the
|
||||
* FOR UPDATE code is able to identify a target table in many cases where
|
||||
* the other code can't, while the non-FOR-UPDATE case allows use of WHERE
|
||||
* CURRENT OF with an insensitive cursor.
|
||||
* 根据游标是否使用,我们有两种不同的策略
|
||||
* 是否更新/共享。支持两者的原因是
|
||||
* 在许多情况下,FOR UPDATE代码能够识别目标表,而其他代码不能,而非FOR-UPDATE情况允许使用不敏感游标的when CURRENT of。
|
||||
*/
|
||||
if (query_desc->estate->es_rowMarks) {
|
||||
ExecRowMark *erm = NULL;
|
||||
ListCell *lc = NULL;
|
||||
|
||||
/*
|
||||
* Here, the query must have exactly one FOR UPDATE/SHARE reference to
|
||||
* the target table, and we dig the ctid info out of that.
|
||||
* 这里,查询必须只有一个对目标表的FOR UPDATE/SHARE引用,我们从中挖掘出ctid信息。
|
||||
*/
|
||||
erm = NULL;
|
||||
foreach (lc, query_desc->estate->es_rowMarks) {
|
||||
ExecRowMark *thiserm = (ExecRowMark *)lfirst(lc);
|
||||
|
||||
if (!RowMarkRequiresRowShareLock(thiserm->markType)) {
|
||||
continue; /* ignore non-FOR UPDATE/SHARE items */
|
||||
continue; /* 忽略非for UPDATE/SHARE项 */
|
||||
}
|
||||
|
||||
if (RelationGetRelid(thiserm->relation) == table_oid) {
|
||||
|
|
@ -124,15 +118,14 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
|
|||
}
|
||||
|
||||
/*
|
||||
* The cursor must have a current result row: per the SQL spec, it's
|
||||
* an error if not.
|
||||
* 游标必须有当前结果行:根据SQL规范,如果没有,则会出现错误。
|
||||
*/
|
||||
if (portal->atStart || portal->atEnd) {
|
||||
ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_STATE),
|
||||
errmsg("cursor \"%s\" is not positioned on a row when the cursor uses for UPDATE/SHARE", cursor_name)));
|
||||
}
|
||||
|
||||
/* Return the currently scanned TID, if there is one */
|
||||
/* 返回当前扫描的TID(如果有) */
|
||||
if (ItemPointerIsValid(&(erm->curCtid))) {
|
||||
*current_tid = erm->curCtid;
|
||||
|
||||
|
|
@ -144,9 +137,7 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
|
|||
}
|
||||
|
||||
/*
|
||||
* This table didn't produce the cursor's current row; some other
|
||||
* inheritance child of the same parent must have. Signal caller to
|
||||
* do nothing on this table.
|
||||
* 这个表没有产生游标的当前行;必须有同一父节点的其他继承子节点。信号调用者在表上什么都不做。
|
||||
*/
|
||||
return false;
|
||||
} else {
|
||||
|
|
@ -156,9 +147,7 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
|
|||
ItemPointer tuple_tid;
|
||||
|
||||
/*
|
||||
* Without FOR UPDATE, we dig through the cursor's plan to find the
|
||||
* scan node. Fail if it's not there or buried underneath
|
||||
* aggregation.
|
||||
* 如果没有FOR UPDATE,我们将通过游标的计划来查找扫描节点。如果不存在或隐藏在聚合下面,则失败。
|
||||
*/
|
||||
scanstate = search_plan_tree(query_desc->planstate, table_oid);
|
||||
if (scanstate == NULL) {
|
||||
|
|
@ -168,23 +157,21 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
|
|||
}
|
||||
|
||||
/*
|
||||
* The cursor must have a current result row: per the SQL spec, it's
|
||||
* an error if not. We test this at the top level, rather than at the
|
||||
* scan node level, because in inheritance cases any one table scan
|
||||
* could easily not be on a row. We want to return false, not raise
|
||||
* error, if the passed-in table OID is for one of the inactive scans.
|
||||
* 游标必须有当前结果行:根据SQL规范,如果没有,则会出现错误。
|
||||
* 我们在顶层测试,而不是在扫描节点级测试,因为在继承情况下,任何一个表扫描都很容易不在一行上。
|
||||
* 如果传入的表OID是用于非活动扫描的,我们希望返回false,而不是引发错误。
|
||||
*/
|
||||
if (portal->atStart || portal->atEnd) {
|
||||
ereport(ERROR, (errcode(ERRCODE_INVALID_CURSOR_STATE), errmsg(
|
||||
"cursor \"%s\" is not positioned on a row when the cursor doesn't use for UPDATE/SHARE", cursor_name)));
|
||||
}
|
||||
|
||||
/* Now OK to return false if we found an inactive scan */
|
||||
/* 现在OK返回false如果我们发现一个非活动扫描 */
|
||||
if (TupIsNull(scanstate->ss_ScanTupleSlot)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Use slot_getattr to catch any possible mistakes */
|
||||
/* 使用slot_getattr捕获任何可能的错误 */
|
||||
tuple_tableoid = DatumGetObjectId(tableam_tslot_getattr(scanstate->ss_ScanTupleSlot, TableOidAttributeNumber, &lisnull));
|
||||
Assert(!lisnull);
|
||||
tuple_tid = (ItemPointer)DatumGetPointer(
|
||||
|
|
@ -206,7 +193,7 @@ bool execCurrentOf(CurrentOfExpr *cexpr, ExprContext *econtext, Relation relatio
|
|||
/*
|
||||
* fetch_cursor_param_value
|
||||
*
|
||||
* Fetch the string value of a param, verifying it is of type REFCURSOR.
|
||||
* 获取参数的字符串值,验证它是REFCURSOR类型。.
|
||||
*/
|
||||
static char *fetch_cursor_param_value(ExprContext *econtext, int paramId)
|
||||
{
|
||||
|
|
@ -215,20 +202,20 @@ static char *fetch_cursor_param_value(ExprContext *econtext, int paramId)
|
|||
if (paramInfo && paramId > 0 && paramId <= paramInfo->numParams) {
|
||||
ParamExternData *prm = ¶mInfo->params[paramId - 1];
|
||||
|
||||
/* give hook a chance in case parameter is dynamic */
|
||||
/* 如果参数是动态的,给钩子一个机会 */
|
||||
if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL) {
|
||||
(*paramInfo->paramFetch)(paramInfo, paramId);
|
||||
}
|
||||
|
||||
if (OidIsValid(prm->ptype) && !prm->isnull) {
|
||||
/* safety check in case hook did something unexpected */
|
||||
/* 安全检查,以防钩子发生意外 */
|
||||
if (prm->ptype != REFCURSOROID) {
|
||||
ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH),
|
||||
errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)", paramId,
|
||||
format_type_be(prm->ptype), format_type_be(REFCURSOROID))));
|
||||
}
|
||||
|
||||
/* We know that refcursor uses text's I/O routines */
|
||||
/* 我们知道refcursor使用text的I/O例程 */
|
||||
return TextDatumGetCString(prm->value);
|
||||
}
|
||||
}
|
||||
|
|
@ -240,8 +227,8 @@ static char *fetch_cursor_param_value(ExprContext *econtext, int paramId)
|
|||
/*
|
||||
* search_plan_tree
|
||||
*
|
||||
* Search through a PlanState tree for a scan node on the specified table.
|
||||
* Return NULL if not found or multiple candidates.
|
||||
* 在PlanState树中搜索指定表上的扫描节点。
|
||||
* 如果没有找到或有多个候选,则返回NULL。
|
||||
*/
|
||||
#ifdef PGXC
|
||||
ScanState* search_plan_tree(PlanState* node, Oid table_oid)
|
||||
|
|
@ -262,7 +249,7 @@ static ScanState* search_plan_tree(PlanState* node, Oid table_oid)
|
|||
}
|
||||
#endif
|
||||
/*
|
||||
* scan nodes can all be treated alike
|
||||
* 扫描节点都可以被同等对待
|
||||
*/
|
||||
case T_SeqScanState:
|
||||
case T_IndexScanState:
|
||||
|
|
@ -284,8 +271,7 @@ static ScanState* search_plan_tree(PlanState* node, Oid table_oid)
|
|||
return result;
|
||||
}
|
||||
/*
|
||||
* For Append, we must look through the members; watch out for
|
||||
* multiple matches (possible if it was from UNION ALL)
|
||||
* 对于Append,必须遍历成员;注意多个匹配(可能来自UNION ALL)
|
||||
*/
|
||||
case T_AppendState: {
|
||||
AppendState *astate = (AppendState *)node;
|
||||
|
|
@ -297,14 +283,14 @@ static ScanState* search_plan_tree(PlanState* node, Oid table_oid)
|
|||
if (elem == NULL)
|
||||
continue;
|
||||
if (result != NULL)
|
||||
return NULL; /* multiple matches */
|
||||
return NULL; /* 多个匹配 */
|
||||
|
||||
result = elem;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/*
|
||||
* Similarly for MergeAppend
|
||||
* 类似于MergeAppend
|
||||
*/
|
||||
case T_MergeAppendState: {
|
||||
MergeAppendState *mstate = (MergeAppendState *)node;
|
||||
|
|
@ -318,15 +304,14 @@ static ScanState* search_plan_tree(PlanState* node, Oid table_oid)
|
|||
continue;
|
||||
}
|
||||
if (result != NULL) {
|
||||
return NULL; /* multiple matches */
|
||||
return NULL; /* 多个匹配 */
|
||||
}
|
||||
result = elem;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/*
|
||||
* Result and Limit can be descended through (these are safe
|
||||
* because they always return their input's current row)
|
||||
* Result和Limit可以依次下降(它们是安全的,因为它们总是返回输入的当前行)
|
||||
*/
|
||||
#ifdef PGXC
|
||||
case T_MaterialState:
|
||||
|
|
@ -337,13 +322,13 @@ static ScanState* search_plan_tree(PlanState* node, Oid table_oid)
|
|||
return search_plan_tree(node->lefttree, table_oid);
|
||||
|
||||
/*
|
||||
* SubqueryScan too, but it keeps the child in a different place
|
||||
* SubqueryScan也可以,但它将子对象保存在不同的位置
|
||||
*/
|
||||
case T_SubqueryScanState:
|
||||
return search_plan_tree(((SubqueryScanState *)node)->subplan, table_oid);
|
||||
|
||||
default:
|
||||
/* Otherwise, assume we can't descend through it */
|
||||
/* 否则,假设我们不能从里面下去 */
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
|
|
|
|||
Loading…
Reference in New Issue