+/*
+ * exec_for_query --- execute body of FOR loop for each row from a portal
+ *
+ * Used by exec_stmt_fors, exec_stmt_forc and exec_stmt_dynfors
+ */
+static int
+exec_for_query(PLpgSQL_execstate *estate, PLpgSQL_stmt_forq *stmt,
+ Portal portal, bool prefetch_ok)
+{
+ PLpgSQL_rec *rec = NULL;
+ PLpgSQL_row *row = NULL;
+ SPITupleTable *tuptab;
+ bool found = false;
+ int rc = PLPGSQL_RC_OK;
+ int n;
+
+ /*
+ * Determine if we assign to a record or a row
+ */
+ if (stmt->rec != NULL)
+ rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->dno]);
+ else if (stmt->row != NULL)
+ row = (PLpgSQL_row *) (estate->datums[stmt->row->dno]);
+ else
+ elog(ERROR, "unsupported target");
+
+ /*
+ * Fetch the initial tuple(s). If prefetching is allowed then we grab
+ * a few more rows to avoid multiple trips through executor startup
+ * overhead.
+ */
+ SPI_cursor_fetch(portal, true, prefetch_ok ? 10 : 1);
+ tuptab = SPI_tuptable;
+ n = SPI_processed;
+
+ /*
+ * If the query didn't return any rows, set the target to NULL and
+ * fall through with found = false.
+ */
+ if (n <= 0)
+ exec_move_row(estate, rec, row, NULL, tuptab->tupdesc);
+ else
+ found = true; /* processed at least one tuple */
+
+ /*
+ * Now do the loop
+ */
+ while (n > 0)
+ {
+ int i;
+
+ for (i = 0; i < n; i++)
+ {
+ /*
+ * Assign the tuple to the target
+ */
+ exec_move_row(estate, rec, row, tuptab->vals[i], tuptab->tupdesc);
+
+ /*
+ * Execute the statements
+ */
+ rc = exec_stmts(estate, stmt->body);
+
+ if (rc != PLPGSQL_RC_OK)
+ {
+ if (rc == PLPGSQL_RC_EXIT)
+ {
+ if (estate->exitlabel == NULL)
+ {
+ /* unlabelled exit, so exit the current loop */
+ rc = PLPGSQL_RC_OK;
+ }
+ else if (stmt->label != NULL &&
+ strcmp(stmt->label, estate->exitlabel) == 0)
+ {
+ /* label matches this loop, so exit loop */
+ estate->exitlabel = NULL;
+ rc = PLPGSQL_RC_OK;
+ }
+
+ /*
+ * otherwise, we processed a labelled exit that does not
+ * match the current statement's label, if any; return
+ * RC_EXIT so that the EXIT continues to recurse upward.
+ */
+ }
+ else if (rc == PLPGSQL_RC_CONTINUE)
+ {
+ if (estate->exitlabel == NULL)
+ {
+ /* unlabelled continue, so re-run the current loop */
+ rc = PLPGSQL_RC_OK;
+ continue;
+ }
+ else if (stmt->label != NULL &&
+ strcmp(stmt->label, estate->exitlabel) == 0)
+ {
+ /* label matches this loop, so re-run loop */
+ estate->exitlabel = NULL;
+ rc = PLPGSQL_RC_OK;
+ continue;
+ }
+
+ /*
+ * otherwise, we process a labelled continue that does not
+ * match the current statement's label, if any; return
+ * RC_CONTINUE so that the CONTINUE will propagate up the
+ * stack.
+ */
+ }
+
+ /*
+ * We're aborting the loop. Need a goto to get out of two
+ * levels of loop...
+ */
+ goto loop_exit;
+ }
+ }
+
+ SPI_freetuptable(tuptab);
+
+ /*
+ * Fetch more tuples. If prefetching is allowed, grab 50 at a time.
+ */
+ SPI_cursor_fetch(portal, true, prefetch_ok ? 50 : 1);
+ tuptab = SPI_tuptable;
+ n = SPI_processed;
+ }
+
+loop_exit:
+
+ /*
+ * Release last group of tuples (if any)
+ */
+ SPI_freetuptable(tuptab);
+
+ /*
+ * Set the FOUND variable to indicate the result of executing the loop
+ * (namely, whether we looped one or more times). This must be set last so
+ * that it does not interfere with the value of the FOUND variable inside
+ * the loop processing itself.
+ */
+ exec_set_found(estate, found);
+
+ return rc;
+}
+
+