+ /* Do it */
+ nprocessed = DoPortalFetch(portal,
+ stmt->direction,
+ stmt->howMany,
+ stmt->ismove ? None : dest);
+
+ /* Return command status if wanted */
+ if (completionTag)
+ snprintf(completionTag, COMPLETION_TAG_BUFSIZE, "%s %ld",
+ stmt->ismove ? "MOVE" : "FETCH",
+ nprocessed);
+}
+
+/*
+ * DoPortalFetch
+ * Guts of PerformPortalFetch --- shared with SPI cursor operations.
+ * Caller must already have validated the Portal.
+ *
+ * Returns number of rows processed (suitable for use in result tag)
+ */
+long
+DoPortalFetch(Portal portal,
+ FetchDirection fdirection,
+ long count,
+ CommandDest dest)
+{
+ bool forward;
+
+ switch (fdirection)
+ {
+ case FETCH_FORWARD:
+ if (count < 0)
+ {
+ fdirection = FETCH_BACKWARD;
+ count = -count;
+ }
+ /* fall out of switch to share code with FETCH_BACKWARD */
+ break;
+ case FETCH_BACKWARD:
+ if (count < 0)
+ {
+ fdirection = FETCH_FORWARD;
+ count = -count;
+ }
+ /* fall out of switch to share code with FETCH_FORWARD */
+ break;
+ case FETCH_ABSOLUTE:
+ if (count > 0)
+ {
+ /*
+ * Definition: Rewind to start, advance count-1 rows, return
+ * next row (if any). In practice, if the goal is less than
+ * halfway back to the start, it's better to scan from where
+ * we are. In any case, we arrange to fetch the target row
+ * going forwards.
+ */
+ if (portal->posOverflow || portal->portalPos == LONG_MAX ||
+ count-1 <= portal->portalPos / 2)
+ {
+ DoPortalRewind(portal);
+ if (count > 1)
+ DoRelativeFetch(portal, true, count-1, None);
+ }
+ else
+ {
+ long pos = portal->portalPos;
+
+ if (portal->atEnd)
+ pos++; /* need one extra fetch if off end */
+ if (count <= pos)
+ DoRelativeFetch(portal, false, pos-count+1, None);
+ else if (count > pos+1)
+ DoRelativeFetch(portal, true, count-pos-1, None);
+ }
+ return DoRelativeFetch(portal, true, 1L, dest);
+ }
+ else if (count < 0)
+ {
+ /*
+ * Definition: Advance to end, back up abs(count)-1 rows,
+ * return prior row (if any). We could optimize this if we
+ * knew in advance where the end was, but typically we won't.
+ * (Is it worth considering case where count > half of size
+ * of query? We could rewind once we know the size ...)
+ */
+ DoRelativeFetch(portal, true, FETCH_ALL, None);
+ if (count < -1)
+ DoRelativeFetch(portal, false, -count-1, None);
+ return DoRelativeFetch(portal, false, 1L, dest);
+ }
+ else /* count == 0 */
+ {
+ /* Rewind to start, return zero rows */
+ DoPortalRewind(portal);
+ return DoRelativeFetch(portal, true, 0L, dest);
+ }
+ break;
+ case FETCH_RELATIVE:
+ if (count > 0)
+ {
+ /*
+ * Definition: advance count-1 rows, return next row (if any).
+ */
+ if (count > 1)
+ DoRelativeFetch(portal, true, count-1, None);
+ return DoRelativeFetch(portal, true, 1L, dest);
+ }
+ else if (count < 0)
+ {
+ /*
+ * Definition: back up abs(count)-1 rows, return prior row
+ * (if any).
+ */
+ if (count < -1)
+ DoRelativeFetch(portal, false, -count-1, None);
+ return DoRelativeFetch(portal, false, 1L, dest);
+ }
+ else /* count == 0 */
+ {
+ /* Same as FETCH FORWARD 0, so fall out of switch */
+ fdirection = FETCH_FORWARD;
+ }
+ break;
+ default:
+ elog(ERROR, "DoPortalFetch: bogus direction");
+ break;
+ }
+