Skip to content

Commit f593f62

Browse files
committed
Fix a couple of places in execMain that erroneously assumed that SELECT FOR
UPDATE/SHARE couldn't occur as a subquery in a query with a non-SELECT top-level operation. Symptoms included outright failure (as in report from Mark Mielke) and silently neglecting to take the requested row locks. Back-patch to 8.3, because the visible failure in the INSERT ... SELECT case is a regression from 8.2. I'm a bit hesitant to back-patch further given the lack of field complaints.
1 parent 819b49a commit f593f62

File tree

1 file changed

+52
-35
lines changed

1 file changed

+52
-35
lines changed

src/backend/executor/execMain.c

Lines changed: 52 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
*
2727
*
2828
* IDENTIFICATION
29-
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.305 2008/03/28 00:21:55 tgl Exp $
29+
* $PostgreSQL: pgsql/src/backend/executor/execMain.c,v 1.306 2008/04/21 03:49:45 tgl Exp $
3030
*
3131
*-------------------------------------------------------------------------
3232
*/
@@ -754,6 +754,16 @@ InitPlan(QueryDesc *queryDesc, int eflags)
754754
*/
755755
estate->es_junkFilter =
756756
estate->es_result_relation_info->ri_junkFilter;
757+
758+
/*
759+
* We currently can't support rowmarks in this case, because
760+
* the associated junk CTIDs might have different resnos in
761+
* different subplans.
762+
*/
763+
if (estate->es_rowMarks)
764+
ereport(ERROR,
765+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
766+
errmsg("SELECT FOR UPDATE/SHARE is not supported within a query with multiple result relations")));
757767
}
758768
else
759769
{
@@ -771,18 +781,6 @@ InitPlan(QueryDesc *queryDesc, int eflags)
771781
{
772782
/* For SELECT, want to return the cleaned tuple type */
773783
tupType = j->jf_cleanTupType;
774-
/* For SELECT FOR UPDATE/SHARE, find the ctid attrs now */
775-
foreach(l, estate->es_rowMarks)
776-
{
777-
ExecRowMark *erm = (ExecRowMark *) lfirst(l);
778-
char resname[32];
779-
780-
snprintf(resname, sizeof(resname), "ctid%u", erm->rti);
781-
erm->ctidAttNo = ExecFindJunkAttribute(j, resname);
782-
if (!AttributeNumberIsValid(erm->ctidAttNo))
783-
elog(ERROR, "could not find junk \"%s\" column",
784-
resname);
785-
}
786784
}
787785
else if (operation == CMD_UPDATE || operation == CMD_DELETE)
788786
{
@@ -791,10 +789,27 @@ InitPlan(QueryDesc *queryDesc, int eflags)
791789
if (!AttributeNumberIsValid(j->jf_junkAttNo))
792790
elog(ERROR, "could not find junk ctid column");
793791
}
792+
793+
/* For SELECT FOR UPDATE/SHARE, find the ctid attrs now */
794+
foreach(l, estate->es_rowMarks)
795+
{
796+
ExecRowMark *erm = (ExecRowMark *) lfirst(l);
797+
char resname[32];
798+
799+
snprintf(resname, sizeof(resname), "ctid%u", erm->rti);
800+
erm->ctidAttNo = ExecFindJunkAttribute(j, resname);
801+
if (!AttributeNumberIsValid(erm->ctidAttNo))
802+
elog(ERROR, "could not find junk \"%s\" column",
803+
resname);
804+
}
794805
}
795806
}
796807
else
808+
{
797809
estate->es_junkFilter = NULL;
810+
if (estate->es_rowMarks)
811+
elog(ERROR, "SELECT FOR UPDATE/SHARE, but no junk columns");
812+
}
798813
}
799814

800815
/*
@@ -1240,47 +1255,30 @@ lnext: ;
12401255
slot = planSlot;
12411256

12421257
/*
1243-
* if we have a junk filter, then project a new tuple with the junk
1258+
* If we have a junk filter, then project a new tuple with the junk
12441259
* removed.
12451260
*
12461261
* Store this new "clean" tuple in the junkfilter's resultSlot.
12471262
* (Formerly, we stored it back over the "dirty" tuple, which is WRONG
12481263
* because that tuple slot has the wrong descriptor.)
12491264
*
1250-
* Also, extract all the junk information we need.
1265+
* But first, extract all the junk information we need.
12511266
*/
12521267
if ((junkfilter = estate->es_junkFilter) != NULL)
12531268
{
1254-
Datum datum;
1255-
bool isNull;
1256-
1257-
/*
1258-
* extract the 'ctid' junk attribute.
1259-
*/
1260-
if (operation == CMD_UPDATE || operation == CMD_DELETE)
1261-
{
1262-
datum = ExecGetJunkAttribute(slot, junkfilter->jf_junkAttNo,
1263-
&isNull);
1264-
/* shouldn't ever get a null result... */
1265-
if (isNull)
1266-
elog(ERROR, "ctid is NULL");
1267-
1268-
tupleid = (ItemPointer) DatumGetPointer(datum);
1269-
tuple_ctid = *tupleid; /* make sure we don't free the ctid!! */
1270-
tupleid = &tuple_ctid;
1271-
}
1272-
12731269
/*
12741270
* Process any FOR UPDATE or FOR SHARE locking requested.
12751271
*/
1276-
else if (estate->es_rowMarks != NIL)
1272+
if (estate->es_rowMarks != NIL)
12771273
{
12781274
ListCell *l;
12791275

12801276
lmark: ;
12811277
foreach(l, estate->es_rowMarks)
12821278
{
12831279
ExecRowMark *erm = lfirst(l);
1280+
Datum datum;
1281+
bool isNull;
12841282
HeapTupleData tuple;
12851283
Buffer buffer;
12861284
ItemPointerData update_ctid;
@@ -1352,6 +1350,25 @@ lnext: ;
13521350
}
13531351
}
13541352

1353+
/*
1354+
* extract the 'ctid' junk attribute.
1355+
*/
1356+
if (operation == CMD_UPDATE || operation == CMD_DELETE)
1357+
{
1358+
Datum datum;
1359+
bool isNull;
1360+
1361+
datum = ExecGetJunkAttribute(slot, junkfilter->jf_junkAttNo,
1362+
&isNull);
1363+
/* shouldn't ever get a null result... */
1364+
if (isNull)
1365+
elog(ERROR, "ctid is NULL");
1366+
1367+
tupleid = (ItemPointer) DatumGetPointer(datum);
1368+
tuple_ctid = *tupleid; /* make sure we don't free the ctid!! */
1369+
tupleid = &tuple_ctid;
1370+
}
1371+
13551372
/*
13561373
* Create a new "clean" tuple with all junk attributes removed. We
13571374
* don't need to do this for DELETE, however (there will in fact

0 commit comments

Comments
 (0)