diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2007-10-24 18:37:09 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2007-10-24 18:37:09 +0000 |
commit | c29a9c37bf6bdaaaa65ccbcd4c69c596691134e1 (patch) | |
tree | c7df7fd2c22d5007ff6901800714d93bc4ce8a4c /src/backend/optimizer | |
parent | 9226ba817b19999d51d39a0a2bde810160d0cf24 (diff) |
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.
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r-- | src/backend/optimizer/path/costsize.c | 32 |
1 files changed, 26 insertions, 6 deletions
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index d1eb29691a0..c722070abc8 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -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 */ @@ -801,6 +805,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)) { |