return heap_attisnull(tuple, attnum);
}
+/*
+ * slot_getsysattr
+ * This function fetches a system attribute of the slot's current tuple.
+ * Unlike slot_getattr, if the slot does not contain system attributes,
+ * this will return false (with a NULL attribute value) instead of
+ * throwing an error.
+ */
+bool
+slot_getsysattr(TupleTableSlot *slot, int attnum,
+ Datum *value, bool *isnull)
+{
+ HeapTuple tuple = slot->tts_tuple;
+
+ Assert(attnum < 0); /* else caller error */
+ if (tuple == NULL ||
+ tuple == &(slot->tts_minhdr))
+ {
+ /* No physical tuple, or minimal tuple, so fail */
+ *value = (Datum) 0;
+ *isnull = true;
+ return false;
+ }
+ *value = heap_getsysattr(tuple, attnum, slot->tts_tupleDescriptor, isnull);
+ return true;
+}
+
/*
* heap_freetuple
*/
*/
#include "postgres.h"
+#include "access/relscan.h"
#include "access/sysattr.h"
#include "catalog/pg_type.h"
#include "executor/executor.h"
}
else
{
- ScanState *scanstate;
- bool lisnull;
- Oid tuple_tableoid PG_USED_FOR_ASSERTS_ONLY;
- 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.
*/
+ ScanState *scanstate;
+
scanstate = search_plan_tree(queryDesc->planstate, table_oid);
if (!scanstate)
ereport(ERROR,
if (TupIsNull(scanstate->ss_ScanTupleSlot))
return false;
- /* Use slot_getattr to catch any possible mistakes */
- tuple_tableoid =
- DatumGetObjectId(slot_getattr(scanstate->ss_ScanTupleSlot,
- TableOidAttributeNumber,
- &lisnull));
- Assert(!lisnull);
- tuple_tid = (ItemPointer)
- DatumGetPointer(slot_getattr(scanstate->ss_ScanTupleSlot,
- SelfItemPointerAttributeNumber,
- &lisnull));
- Assert(!lisnull);
-
- Assert(tuple_tableoid == table_oid);
-
- *current_tid = *tuple_tid;
+ /*
+ * Extract TID of the scan's current row. The mechanism for this is
+ * in principle scan-type-dependent, but for most scan types, we can
+ * just dig the TID out of the physical scan tuple.
+ */
+ if (IsA(scanstate, IndexOnlyScanState))
+ {
+ /*
+ * For IndexOnlyScan, the tuple stored in ss_ScanTupleSlot may be
+ * a virtual tuple that does not have the ctid column, so we have
+ * to get the TID from xs_ctup.t_self.
+ */
+ IndexScanDesc scan = ((IndexOnlyScanState *) scanstate)->ioss_ScanDesc;
+
+ *current_tid = scan->xs_ctup.t_self;
+ }
+ else
+ {
+ /*
+ * Default case: try to fetch TID from the scan node's current
+ * tuple. As an extra cross-check, verify tableoid in the current
+ * tuple. If the scan hasn't provided a physical tuple, we have
+ * to fail.
+ */
+ Datum ldatum;
+ bool lisnull;
+ ItemPointer tuple_tid;
+
+#ifdef USE_ASSERT_CHECKING
+ if (!slot_getsysattr(scanstate->ss_ScanTupleSlot,
+ TableOidAttributeNumber,
+ &ldatum,
+ &lisnull))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_CURSOR_STATE),
+ errmsg("cursor \"%s\" is not a simply updatable scan of table \"%s\"",
+ cursor_name, table_name)));
+ Assert(!lisnull);
+ Assert(DatumGetObjectId(ldatum) == table_oid);
+#endif
+
+ if (!slot_getsysattr(scanstate->ss_ScanTupleSlot,
+ SelfItemPointerAttributeNumber,
+ &ldatum,
+ &lisnull))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_CURSOR_STATE),
+ errmsg("cursor \"%s\" is not a simply updatable scan of table \"%s\"",
+ cursor_name, table_name)));
+ Assert(!lisnull);
+ tuple_tid = (ItemPointer) DatumGetPointer(ldatum);
+
+ *current_tid = *tuple_tid;
+ }
+
+ Assert(ItemPointerIsValid(current_tid));
return true;
}
DELETE FROM ucview WHERE CURRENT OF c1; -- fail, views not supported
ERROR: WHERE CURRENT OF on a view is not implemented
+ROLLBACK;
+-- Check WHERE CURRENT OF with an index-only scan
+BEGIN;
+EXPLAIN (costs off)
+DECLARE c1 CURSOR FOR SELECT stringu1 FROM onek WHERE stringu1 = 'DZAAAA';
+ QUERY PLAN
+---------------------------------------------
+ Index Only Scan using onek_stringu1 on onek
+ Index Cond: (stringu1 = 'DZAAAA'::name)
+(2 rows)
+
+DECLARE c1 CURSOR FOR SELECT stringu1 FROM onek WHERE stringu1 = 'DZAAAA';
+FETCH FROM c1;
+ stringu1
+----------
+ DZAAAA
+(1 row)
+
+DELETE FROM onek WHERE CURRENT OF c1;
+SELECT stringu1 FROM onek WHERE stringu1 = 'DZAAAA';
+ stringu1
+----------
+(0 rows)
+
ROLLBACK;
-- Make sure snapshot management works okay, per bug report in
-- 235395b90909301035v7228ce63q392931f15aa74b31@mail.gmail.com