Greetings, * Pavel Stehule (pavel.steh...@gmail.com) wrote: > This patch remove redundant rows from PL/pgSQL executor (-89 lines).
While I can certainly appreciate wanting to remove redundant code, I don't think this change actually improves the situation. The problem is more than just that we might want to make a change to 'while' but not 'for', it's also that it makes it very difficult to follow the code flow. If you could have found a way to make it work for all calls to exec_stmts(), it might be a bit better, but you can't without kludging it up further. If you could, then it might be possible to move some of this logic *into* exec_stmts(), but I don't see that being reasonable either. In the end, I'd recommend cleaning up the handling of the exec_stmts() return code so that all of these pieces follow the same style and look similar (I'd go with the switch-based approach and remove the if/else branches). That'll make it easier for anyone coming along later who does end up needing to change all three. > Doesn't change a functionality. I'm not convinced of this either, to be honest.. In exec_stmt_while(), there is: for(;;) { [...] if (isnull || !value) break; rc = exec_stmts(estate, stmt->body); [...] } return PLPGSQL_RC_OK; You replaced the last return with: return rc; Which could mean returning an uninitialized rc if the above break; happens. In the end, I guess it's up to the committers on how they feel about this, so here's an updated patch which fixes the above issue and improves the comments/grammer. Passes all regressions and works in my limited testing. commit e6639d83db5b301e184bf158b1591007a3f79526 Author: Stephen Frost <sfr...@snowman.net> Date: Wed Jan 19 14:28:36 2011 -0500 Improve comments in pl_exec.c wrt can_leave_loop() This patch improves some of the comments around can_leave_loop(), and fixes a couple of fall-through cases to make sure they behave the same as before the code de-duplication patch that introduced can_leave_loop(). commit f8262adcba662164dbc24d289cb036b3f0aee582 Author: Stephen Frost <sfr...@snowman.net> Date: Wed Jan 19 14:26:27 2011 -0500 remove redundant code from pl_exec.c This patch removes redundant handling of exec_stmts()'s return code by creating a new function to be called after exec_stmts() is run. This new function will then handle the return code, update it if necessary, and return if the loop should continue or not. Patch by Pavel Stehule. Thanks, Stephen
*** a/src/pl/plpgsql/src/pl_exec.c --- b/src/pl/plpgsql/src/pl_exec.c *************** *** 204,209 **** static Portal exec_dynquery_with_params(PLpgSQL_execstate *estate, --- 204,211 ---- PLpgSQL_expr *dynquery, List *params, const char *portalname, int cursorOptions); + static bool can_leave_loop(PLpgSQL_execstate *estate, + PLpgSQL_any_loop *stmt, int *rc); /* ---------- * plpgsql_exec_function Called by the call handler for *************** *** 1566,1571 **** exec_stmt_case(PLpgSQL_execstate *estate, PLpgSQL_stmt_case *stmt) --- 1568,1650 ---- return exec_stmts(estate, stmt->else_stmts); } + /* + * can_leave_loop + * + * Check the result of exec_stmts for the various exec_stmt_loop + * functions (unconditional loop, while loop, for-over-integer loop, + * for-over-portal loop). + * + * Returns true when the outer cycle should be left, otherwise false. + * Will also update the return code (rc) as necessary. + */ + static bool + can_leave_loop(PLpgSQL_execstate *estate, PLpgSQL_any_loop *stmt, int *rc) + { + /* + * Check which return code exec_stmts() returned and handle it + * accordingly. + */ + switch (*rc) + { + case PLPGSQL_RC_OK: + /* Just continue the outer loop on PLPGSQL_RC_OK */ + return false; + + case PLPGSQL_RC_RETURN: + /* Time to break out of the outer loop */ + return true; + + case PLPGSQL_RC_EXIT: + if (estate->exitlabel == NULL) + { + /* unlabelled exit, finish the current loop */ + *rc = PLPGSQL_RC_OK; + } + 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. + */ + return true; + + case PLPGSQL_RC_CONTINUE: + if (estate->exitlabel == NULL) + { + /* anonymous continue, so re-run the 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. + */ + return true; + } + + return false; + + default: + elog(ERROR, "unrecognized rc: %d", *rc); + return false; /* keep compiler quiet */ + } + } /* ---------- * exec_stmt_loop Loop over statements until *************** *** 1575,1620 **** exec_stmt_case(PLpgSQL_execstate *estate, PLpgSQL_stmt_case *stmt) static int exec_stmt_loop(PLpgSQL_execstate *estate, PLpgSQL_stmt_loop *stmt) { 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; ! default: ! elog(ERROR, "unrecognized rc: %d", rc); ! } } return PLPGSQL_RC_OK; } --- 1654,1670 ---- static int exec_stmt_loop(PLpgSQL_execstate *estate, PLpgSQL_stmt_loop *stmt) { + int rc; + for (;;) { ! rc = exec_stmts(estate, stmt->body); ! if (can_leave_loop(estate, (PLpgSQL_any_loop *) stmt, &rc)) ! return rc; } + /* Keep compiler happy, but we should never get here */ return PLPGSQL_RC_OK; } *************** *** 1628,1636 **** exec_stmt_loop(PLpgSQL_execstate *estate, PLpgSQL_stmt_loop *stmt) static int exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt) { for (;;) { - int rc; bool value; bool isnull; --- 1678,1687 ---- static int exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt) { + int rc; + for (;;) { bool value; bool isnull; *************** *** 1642,1683 **** exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt) 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); ! } } return PLPGSQL_RC_OK; } --- 1693,1703 ---- rc = exec_stmts(estate, stmt->body); ! if (can_leave_loop(estate, (PLpgSQL_any_loop *) stmt, &rc)) ! return rc; } + /* In case we fall through from above */ return PLPGSQL_RC_OK; } *************** *** 1789,1838 **** exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt) */ 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; - } - } /* * Increase/decrease loop value, unless it would overflow, in which --- 1809,1816 ---- */ rc = exec_stmts(estate, stmt->body); ! if (can_leave_loop(estate, (PLpgSQL_any_loop *) stmt, &rc)) break; /* * Increase/decrease loop value, unless it would overflow, in which *************** *** 4410,4469 **** exec_for_query(PLpgSQL_execstate *estate, PLpgSQL_stmt_forq *stmt, */ 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); --- 4388,4395 ---- */ rc = exec_stmts(estate, stmt->body); ! if (can_leave_loop(estate, (PLpgSQL_any_loop *) stmt, &rc)) goto loop_exit; } SPI_freetuptable(tuptab); *** a/src/pl/plpgsql/src/plpgsql.h --- b/src/pl/plpgsql/src/plpgsql.h *************** *** 407,412 **** typedef struct /* one arm of CASE statement */ --- 407,420 ---- } PLpgSQL_case_when; + typedef struct /* Ancestor of loops */ + { + int cmd_type; + int lineno; + char *label; + } PLpgSQL_any_loop; + + typedef struct { /* Unconditional LOOP statement */ int cmd_type;
signature.asc
Description: Digital signature