mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Fix UPDATE/DELETE WHERE CURRENT OF to support repeated update and update-
then-delete on the current cursor row. The basic fix is that nodeTidscan.c has to apply heap_get_latest_tid() to the current-scan-TID obtained from the cursor query; this ensures we get the latest row version to work with. However, since that only works if the query plan is a TID scan, we also have to hack the planner to make sure only that type of plan will be selected. (Formerly, the planner might decide to apply a seqscan if the table is very small. This change is probably a Good Thing anyway, since it's hard to see how a seqscan could really win.) That means the execQual.c code to support CurrentOfExpr as a regular expression type is dead code, so replace it with just an elog(). Also, add regression tests covering these cases. Note that the added tests expose the fact that re-fetching an updated row misbehaves if the cursor used FOR UPDATE. That's an independent bug that should be fixed later. Per report from Dharmendra Goyal.
This commit is contained in:
@ -54,7 +54,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.186 2007/09/22 21:36:40 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.187 2007/10/24 18:37:08 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -769,6 +769,7 @@ cost_tidscan(Path *path, PlannerInfo *root,
|
||||
{
|
||||
Cost startup_cost = 0;
|
||||
Cost run_cost = 0;
|
||||
bool isCurrentOf = false;
|
||||
Cost cpu_per_tuple;
|
||||
QualCost tid_qual_cost;
|
||||
int ntuples;
|
||||
@ -778,9 +779,6 @@ cost_tidscan(Path *path, PlannerInfo *root,
|
||||
Assert(baserel->relid > 0);
|
||||
Assert(baserel->rtekind == RTE_RELATION);
|
||||
|
||||
if (!enable_tidscan)
|
||||
startup_cost += disable_cost;
|
||||
|
||||
/* Count how many tuples we expect to retrieve */
|
||||
ntuples = 0;
|
||||
foreach(l, tidquals)
|
||||
@ -793,6 +791,12 @@ cost_tidscan(Path *path, PlannerInfo *root,
|
||||
|
||||
ntuples += estimate_array_length(arraynode);
|
||||
}
|
||||
else if (IsA(lfirst(l), CurrentOfExpr))
|
||||
{
|
||||
/* CURRENT OF yields 1 tuple */
|
||||
isCurrentOf = true;
|
||||
ntuples++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* It's just CTID = something, count 1 tuple */
|
||||
@ -800,6 +804,22 @@ cost_tidscan(Path *path, PlannerInfo *root,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We must force TID scan for WHERE CURRENT OF, because only nodeTidscan.c
|
||||
* understands how to do it correctly. Therefore, honor enable_tidscan
|
||||
* only when CURRENT OF isn't present. Also note that cost_qual_eval
|
||||
* counts a CurrentOfExpr as having startup cost disable_cost, which we
|
||||
* subtract off here; that's to prevent other plan types such as seqscan
|
||||
* from winning.
|
||||
*/
|
||||
if (isCurrentOf)
|
||||
{
|
||||
Assert(baserel->baserestrictcost.startup >= disable_cost);
|
||||
startup_cost -= disable_cost;
|
||||
}
|
||||
else if (!enable_tidscan)
|
||||
startup_cost += disable_cost;
|
||||
|
||||
/*
|
||||
* The TID qual expressions will be computed once, any other baserestrict
|
||||
* quals once per retrived tuple.
|
||||
@ -2002,8 +2022,8 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
|
||||
}
|
||||
else if (IsA(node, CurrentOfExpr))
|
||||
{
|
||||
/* This is noticeably more expensive than a typical operator */
|
||||
context->total.per_tuple += 100 * cpu_operator_cost;
|
||||
/* Report high cost to prevent selection of anything but TID scan */
|
||||
context->total.startup += disable_cost;
|
||||
}
|
||||
else if (IsA(node, SubLink))
|
||||
{
|
||||
|
Reference in New Issue
Block a user