static MemoryContext shared_cast_context = NULL;
static HTAB *shared_cast_hash = NULL;
+/*
+ * LOOP_RC_PROCESSING encapsulates common logic for looping statements to
+ * handle return/exit/continue result codes from the loop body statement(s).
+ * It's meant to be used like this:
+ *
+ * int rc = PLPGSQL_RC_OK;
+ * for (...)
+ * {
+ * ...
+ * rc = exec_stmts(estate, stmt->body);
+ * LOOP_RC_PROCESSING(stmt->label, break);
+ * ...
+ * }
+ * return rc;
+ *
+ * If execution of the loop should terminate, LOOP_RC_PROCESSING will execute
+ * "exit_action" (typically a "break" or "goto"), after updating "rc" to the
+ * value the current statement should return. If execution should continue,
+ * LOOP_RC_PROCESSING will do nothing except reset "rc" to PLPGSQL_RC_OK.
+ *
+ * estate and rc are implicit arguments to the macro.
+ * estate->exitlabel is examined and possibly updated.
+ */
+#define LOOP_RC_PROCESSING(looplabel, exit_action) \
+ if (rc == PLPGSQL_RC_RETURN) \
+ { \
+ /* RETURN, so propagate RC_RETURN out */ \
+ exit_action; \
+ } \
+ else if (rc == PLPGSQL_RC_EXIT) \
+ { \
+ if (estate->exitlabel == NULL) \
+ { \
+ /* unlabelled EXIT terminates this loop */ \
+ rc = PLPGSQL_RC_OK; \
+ exit_action; \
+ } \
+ else if ((looplabel) != NULL && \
+ strcmp(looplabel, estate->exitlabel) == 0) \
+ { \
+ /* labelled EXIT matching this loop, so terminate loop */ \
+ estate->exitlabel = NULL; \
+ rc = PLPGSQL_RC_OK; \
+ exit_action; \
+ } \
+ else \
+ { \
+ /* non-matching labelled EXIT, propagate RC_EXIT out */ \
+ exit_action; \
+ } \
+ } \
+ else if (rc == PLPGSQL_RC_CONTINUE) \
+ { \
+ if (estate->exitlabel == NULL) \
+ { \
+ /* unlabelled CONTINUE matches this loop, so continue in loop */ \
+ rc = PLPGSQL_RC_OK; \
+ } \
+ else if ((looplabel) != NULL && \
+ strcmp(looplabel, estate->exitlabel) == 0) \
+ { \
+ /* labelled CONTINUE matching this loop, so continue in loop */ \
+ estate->exitlabel = NULL; \
+ rc = PLPGSQL_RC_OK; \
+ } \
+ else \
+ { \
+ /* non-matching labelled CONTINUE, propagate RC_CONTINUE out */ \
+ exit_action; \
+ } \
+ } \
+ else \
+ Assert(rc == PLPGSQL_RC_OK)
+
/************************************************************
* Local function forward declarations
************************************************************/
estate->err_text = NULL;
/*
- * Handle the return code.
+ * Handle the return code. This is intentionally different from
+ * LOOP_RC_PROCESSING(): CONTINUE never matches a block, and EXIT matches
+ * a block only if there is a label match.
*/
switch (rc)
{
return rc;
case PLPGSQL_RC_EXIT:
-
- /*
- * This is intentionally different from the handling of RC_EXIT
- * for loops: to match a block, we require a match by label.
- */
if (estate->exitlabel == NULL)
return PLPGSQL_RC_EXIT;
if (block->label == NULL)
static int
exec_stmt_loop(PLpgSQL_execstate *estate, PLpgSQL_stmt_loop *stmt)
{
+ int rc = PLPGSQL_RC_OK;
+
for (;;)
{
- int rc = exec_stmts(estate, stmt->body);
-
- switch (rc)
- {
- case PLPGSQL_RC_OK:
- break;
-
- case PLPGSQL_RC_EXIT:
- if (estate->exitlabel == NULL)
- return PLPGSQL_RC_OK;
- if (stmt->label == NULL)
- return PLPGSQL_RC_EXIT;
- if (strcmp(stmt->label, estate->exitlabel) != 0)
- return PLPGSQL_RC_EXIT;
- estate->exitlabel = NULL;
- return PLPGSQL_RC_OK;
-
- case PLPGSQL_RC_CONTINUE:
- if (estate->exitlabel == NULL)
- /* anonymous continue, so re-run the loop */
- break;
- else if (stmt->label != NULL &&
- strcmp(stmt->label, estate->exitlabel) == 0)
- /* label matches named continue, so re-run loop */
- estate->exitlabel = NULL;
- else
- /* label doesn't match named continue, so propagate upward */
- return PLPGSQL_RC_CONTINUE;
- break;
-
- case PLPGSQL_RC_RETURN:
- return rc;
+ rc = exec_stmts(estate, stmt->body);
- default:
- elog(ERROR, "unrecognized rc: %d", rc);
- }
+ LOOP_RC_PROCESSING(stmt->label, break);
}
+
+ return rc;
}
static int
exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt)
{
+ int rc = PLPGSQL_RC_OK;
+
for (;;)
{
- int rc;
bool value;
bool isnull;
rc = exec_stmts(estate, stmt->body);
- switch (rc)
- {
- case PLPGSQL_RC_OK:
- break;
-
- case PLPGSQL_RC_EXIT:
- if (estate->exitlabel == NULL)
- return PLPGSQL_RC_OK;
- if (stmt->label == NULL)
- return PLPGSQL_RC_EXIT;
- if (strcmp(stmt->label, estate->exitlabel) != 0)
- return PLPGSQL_RC_EXIT;
- estate->exitlabel = NULL;
- return PLPGSQL_RC_OK;
-
- case PLPGSQL_RC_CONTINUE:
- if (estate->exitlabel == NULL)
- /* anonymous continue, so re-run loop */
- break;
- else if (stmt->label != NULL &&
- strcmp(stmt->label, estate->exitlabel) == 0)
- /* label matches named continue, so re-run loop */
- estate->exitlabel = NULL;
- else
- /* label doesn't match named continue, propagate upward */
- return PLPGSQL_RC_CONTINUE;
- break;
-
- case PLPGSQL_RC_RETURN:
- return rc;
-
- default:
- elog(ERROR, "unrecognized rc: %d", rc);
- }
+ LOOP_RC_PROCESSING(stmt->label, break);
}
- return PLPGSQL_RC_OK;
+ return rc;
}
*/
rc = exec_stmts(estate, stmt->body);
- if (rc == PLPGSQL_RC_RETURN)
- break; /* break out of the loop */
- else if (rc == PLPGSQL_RC_EXIT)
- {
- if (estate->exitlabel == NULL)
- /* unlabelled exit, finish the current loop */
- rc = PLPGSQL_RC_OK;
- else if (stmt->label != NULL &&
- strcmp(stmt->label, estate->exitlabel) == 0)
- {
- /* labelled exit, matches the current stmt's label */
- estate->exitlabel = NULL;
- rc = PLPGSQL_RC_OK;
- }
-
- /*
- * otherwise, this is a labelled exit that does not match the
- * current statement's label, if any: return RC_EXIT so that the
- * EXIT continues to propagate up the stack.
- */
- break;
- }
- else if (rc == PLPGSQL_RC_CONTINUE)
- {
- if (estate->exitlabel == NULL)
- /* unlabelled continue, so re-run the current loop */
- rc = PLPGSQL_RC_OK;
- else if (stmt->label != NULL &&
- strcmp(stmt->label, estate->exitlabel) == 0)
- {
- /* label matches named continue, so re-run loop */
- estate->exitlabel = NULL;
- rc = PLPGSQL_RC_OK;
- }
- else
- {
- /*
- * otherwise, this is a named continue that does not match the
- * current statement's label, if any: return RC_CONTINUE so
- * that the CONTINUE will propagate up the stack.
- */
- break;
- }
- }
+ LOOP_RC_PROCESSING(stmt->label, break);
/*
* Increase/decrease loop value, unless it would overflow, in which
*/
rc = exec_stmts(estate, stmt->body);
- /* Handle the return code */
- if (rc == PLPGSQL_RC_RETURN)
- break; /* break out of the loop */
- else if (rc == PLPGSQL_RC_EXIT)
- {
- if (estate->exitlabel == NULL)
- /* unlabelled exit, finish the current loop */
- rc = PLPGSQL_RC_OK;
- else if (stmt->label != NULL &&
- strcmp(stmt->label, estate->exitlabel) == 0)
- {
- /* labelled exit, matches the current stmt's label */
- estate->exitlabel = NULL;
- rc = PLPGSQL_RC_OK;
- }
-
- /*
- * otherwise, this is a labelled exit that does not match the
- * current statement's label, if any: return RC_EXIT so that the
- * EXIT continues to propagate up the stack.
- */
- break;
- }
- else if (rc == PLPGSQL_RC_CONTINUE)
- {
- if (estate->exitlabel == NULL)
- /* unlabelled continue, so re-run the current loop */
- rc = PLPGSQL_RC_OK;
- else if (stmt->label != NULL &&
- strcmp(stmt->label, estate->exitlabel) == 0)
- {
- /* label matches named continue, so re-run loop */
- estate->exitlabel = NULL;
- rc = PLPGSQL_RC_OK;
- }
- else
- {
- /*
- * otherwise, this is a named continue that does not match the
- * current statement's label, if any: return RC_CONTINUE so
- * that the CONTINUE will propagate up the stack.
- */
- break;
- }
- }
+ LOOP_RC_PROCESSING(stmt->label, break);
MemoryContextSwitchTo(stmt_mcontext);
}
*/
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;
- }
+ LOOP_RC_PROCESSING(stmt->label, goto loop_exit);
}
SPI_freetuptable(tuptab);