*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.222 2007/09/06 17:31:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.223 2007/10/24 18:37:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* ----------------------------------------------------------------
* ExecEvalCurrentOfExpr
*
- * Normally, the planner will convert CURRENT OF into a TidScan qualification,
- * but we have plain execQual support in case it doesn't.
+ * The planner must convert CURRENT OF into a TidScan qualification.
+ * So, we have to be able to do ExecInitExpr on a CurrentOfExpr,
+ * but we shouldn't ever actually execute it.
* ----------------------------------------------------------------
*/
static Datum
ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone)
{
- CurrentOfExpr *cexpr = (CurrentOfExpr *) exprstate->expr;
- bool result;
- bool lisnull;
- Oid tableoid;
- ItemPointer tuple_tid;
- ItemPointerData cursor_tid;
-
- if (isDone)
- *isDone = ExprSingleResult;
- *isNull = false;
-
- Assert(cexpr->cvarno != INNER);
- Assert(cexpr->cvarno != OUTER);
- Assert(!TupIsNull(econtext->ecxt_scantuple));
- /* Use slot_getattr to catch any possible mistakes */
- tableoid = DatumGetObjectId(slot_getattr(econtext->ecxt_scantuple,
- TableOidAttributeNumber,
- &lisnull));
- Assert(!lisnull);
- tuple_tid = (ItemPointer)
- DatumGetPointer(slot_getattr(econtext->ecxt_scantuple,
- SelfItemPointerAttributeNumber,
- &lisnull));
- Assert(!lisnull);
-
- if (execCurrentOf(cexpr, econtext, tableoid, &cursor_tid))
- result = ItemPointerEquals(&cursor_tid, tuple_tid);
- else
- result = false;
-
- return BoolGetDatum(result);
+ elog(ERROR, "CURRENT OF cannot be executed");
+ return 0; /* keep compiler quiet */
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/nodeTidscan.c,v 1.55 2007/06/11 22:22:40 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/nodeTidscan.c,v 1.56 2007/10/24 18:37:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
tidList = (ItemPointerData *)
palloc(numAllocTids * sizeof(ItemPointerData));
numTids = 0;
+ tidstate->tss_isCurrentOf = false;
foreach(l, evalList)
{
numAllocTids * sizeof(ItemPointerData));
}
tidList[numTids++] = cursor_tid;
+ tidstate->tss_isCurrentOf = true;
}
}
else
int lastTid;
int i;
+ /* CurrentOfExpr could never appear OR'd with something else */
+ Assert(!tidstate->tss_isCurrentOf);
+
qsort((void *) tidList, numTids, sizeof(ItemPointerData),
itemptr_comparator);
lastTid = 0;
/*
* XXX shouldn't we check here to make sure tuple matches TID list? In
- * runtime-key case this is not certain, is it?
+ * runtime-key case this is not certain, is it? However, in the
+ * WHERE CURRENT OF case it might not match anyway ...
*/
ExecStoreTuple(estate->es_evTuple[scanrelid - 1],
while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids)
{
tuple->t_self = tidList[node->tss_TidPtr];
+
+ /*
+ * For WHERE CURRENT OF, the tuple retrieved from the cursor might
+ * since have been updated; if so, we should fetch the version that
+ * is current according to our snapshot.
+ */
+ if (node->tss_isCurrentOf)
+ heap_get_latest_tid(heapRelation, snapshot, &tuple->t_self);
+
if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
{
/*
* 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 $
*
*-------------------------------------------------------------------------
*/
{
Cost startup_cost = 0;
Cost run_cost = 0;
+ bool isCurrentOf = false;
Cost cpu_per_tuple;
QualCost tid_qual_cost;
int ntuples;
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)
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 */
}
}
+ /*
+ * 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.
}
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))
{
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.178 2007/09/20 17:56:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.179 2007/10/24 18:37:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/* ----------------
* TidScanState information
*
+ * isCurrentOf scan has a CurrentOfExpr qual
* NumTids number of tids in this scan
* TidPtr index of currently fetched tid
* TidList evaluated item pointers (array of size NumTids)
{
ScanState ss; /* its first field is NodeTag */
List *tss_tidquals; /* list of ExprState nodes */
+ bool tss_isCurrentOf;
int tss_NumTids;
int tss_TidPtr;
int tss_MarkTidPtr;
8 | one
(2 rows)
+-- Check repeated-update and update-then-delete cases
+BEGIN;
+DECLARE c1 CURSOR FOR SELECT * FROM uctest;
+FETCH c1;
+ f1 | f2
+----+-------
+ 3 | three
+(1 row)
+
+UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1;
+SELECT * FROM uctest;
+ f1 | f2
+----+-------
+ 8 | one
+ 13 | three
+(2 rows)
+
+UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1;
+SELECT * FROM uctest;
+ f1 | f2
+----+-------
+ 8 | one
+ 23 | three
+(2 rows)
+
+-- insensitive cursor should not show effects of updates or deletes
+FETCH RELATIVE 0 FROM c1;
+ f1 | f2
+----+-------
+ 3 | three
+(1 row)
+
+DELETE FROM uctest WHERE CURRENT OF c1;
+SELECT * FROM uctest;
+ f1 | f2
+----+-----
+ 8 | one
+(1 row)
+
+DELETE FROM uctest WHERE CURRENT OF c1; -- no-op
+SELECT * FROM uctest;
+ f1 | f2
+----+-----
+ 8 | one
+(1 row)
+
+UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -- no-op
+SELECT * FROM uctest;
+ f1 | f2
+----+-----
+ 8 | one
+(1 row)
+
+FETCH RELATIVE 0 FROM c1;
+ f1 | f2
+----+-------
+ 3 | three
+(1 row)
+
+ROLLBACK;
+SELECT * FROM uctest;
+ f1 | f2
+----+-------
+ 3 | three
+ 8 | one
+(2 rows)
+
+BEGIN;
+DECLARE c1 CURSOR FOR SELECT * FROM uctest FOR UPDATE;
+FETCH c1;
+ f1 | f2
+----+-------
+ 3 | three
+(1 row)
+
+UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1;
+SELECT * FROM uctest;
+ f1 | f2
+----+-------
+ 8 | one
+ 13 | three
+(2 rows)
+
+UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1;
+SELECT * FROM uctest;
+ f1 | f2
+----+-------
+ 8 | one
+ 23 | three
+(2 rows)
+
+-- sensitive cursor should show effects of updates or deletes
+-- XXX current behavior is WRONG
+FETCH RELATIVE 0 FROM c1;
+ f1 | f2
+----+-----
+ 8 | one
+(1 row)
+
+DELETE FROM uctest WHERE CURRENT OF c1;
+SELECT * FROM uctest;
+ f1 | f2
+----+-------
+ 23 | three
+(1 row)
+
+DELETE FROM uctest WHERE CURRENT OF c1; -- no-op
+SELECT * FROM uctest;
+ f1 | f2
+----+-------
+ 23 | three
+(1 row)
+
+UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -- no-op
+SELECT * FROM uctest;
+ f1 | f2
+----+-------
+ 23 | three
+(1 row)
+
+FETCH RELATIVE 0 FROM c1;
+ f1 | f2
+----+----
+(0 rows)
+
+ROLLBACK;
+SELECT * FROM uctest;
+ f1 | f2
+----+-------
+ 3 | three
+ 8 | one
+(2 rows)
+
-- Check inheritance cases
CREATE TEMP TABLE ucchild () inherits (uctest);
INSERT INTO ucchild values(100, 'hundred');
COMMIT;
SELECT * FROM uctest;
+-- Check repeated-update and update-then-delete cases
+BEGIN;
+DECLARE c1 CURSOR FOR SELECT * FROM uctest;
+FETCH c1;
+UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1;
+SELECT * FROM uctest;
+UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1;
+SELECT * FROM uctest;
+-- insensitive cursor should not show effects of updates or deletes
+FETCH RELATIVE 0 FROM c1;
+DELETE FROM uctest WHERE CURRENT OF c1;
+SELECT * FROM uctest;
+DELETE FROM uctest WHERE CURRENT OF c1; -- no-op
+SELECT * FROM uctest;
+UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -- no-op
+SELECT * FROM uctest;
+FETCH RELATIVE 0 FROM c1;
+ROLLBACK;
+SELECT * FROM uctest;
+
+BEGIN;
+DECLARE c1 CURSOR FOR SELECT * FROM uctest FOR UPDATE;
+FETCH c1;
+UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1;
+SELECT * FROM uctest;
+UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1;
+SELECT * FROM uctest;
+-- sensitive cursor should show effects of updates or deletes
+-- XXX current behavior is WRONG
+FETCH RELATIVE 0 FROM c1;
+DELETE FROM uctest WHERE CURRENT OF c1;
+SELECT * FROM uctest;
+DELETE FROM uctest WHERE CURRENT OF c1; -- no-op
+SELECT * FROM uctest;
+UPDATE uctest SET f1 = f1 + 10 WHERE CURRENT OF c1; -- no-op
+SELECT * FROM uctest;
+FETCH RELATIVE 0 FROM c1;
+ROLLBACK;
+SELECT * FROM uctest;
+
-- Check inheritance cases
CREATE TEMP TABLE ucchild () inherits (uctest);
INSERT INTO ucchild values(100, 'hundred');