Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 3e724aa

Browse files
committed
Merge coding of return/exit/continue cases in plpgsql's loop statements.
plpgsql's five different loop control statements contained three distinct implementations of the same (or what ought to be the same, at least) logic for handling return/exit/continue result codes from their child statements. At best, that's trouble waiting to happen, and there seems no very good reason for the coding to be so different. Refactor so that all the common logic is expressed in a single macro. Discussion: https://postgr.es/m/26314.1514670401@sss.pgh.pa.us
1 parent dd2243f commit 3e724aa

File tree

1 file changed

+90
-220
lines changed

1 file changed

+90
-220
lines changed

src/pl/plpgsql/src/pl_exec.c

Lines changed: 90 additions & 220 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,80 @@ typedef struct /* cast_hash table entry */
155155
static MemoryContext shared_cast_context = NULL;
156156
static HTAB *shared_cast_hash = NULL;
157157

158+
/*
159+
* LOOP_RC_PROCESSING encapsulates common logic for looping statements to
160+
* handle return/exit/continue result codes from the loop body statement(s).
161+
* It's meant to be used like this:
162+
*
163+
* int rc = PLPGSQL_RC_OK;
164+
* for (...)
165+
* {
166+
* ...
167+
* rc = exec_stmts(estate, stmt->body);
168+
* LOOP_RC_PROCESSING(stmt->label, break);
169+
* ...
170+
* }
171+
* return rc;
172+
*
173+
* If execution of the loop should terminate, LOOP_RC_PROCESSING will execute
174+
* "exit_action" (typically a "break" or "goto"), after updating "rc" to the
175+
* value the current statement should return. If execution should continue,
176+
* LOOP_RC_PROCESSING will do nothing except reset "rc" to PLPGSQL_RC_OK.
177+
*
178+
* estate and rc are implicit arguments to the macro.
179+
* estate->exitlabel is examined and possibly updated.
180+
*/
181+
#define LOOP_RC_PROCESSING(looplabel, exit_action) \
182+
if (rc == PLPGSQL_RC_RETURN) \
183+
{ \
184+
/* RETURN, so propagate RC_RETURN out */ \
185+
exit_action; \
186+
} \
187+
else if (rc == PLPGSQL_RC_EXIT) \
188+
{ \
189+
if (estate->exitlabel == NULL) \
190+
{ \
191+
/* unlabelled EXIT terminates this loop */ \
192+
rc = PLPGSQL_RC_OK; \
193+
exit_action; \
194+
} \
195+
else if ((looplabel) != NULL && \
196+
strcmp(looplabel, estate->exitlabel) == 0) \
197+
{ \
198+
/* labelled EXIT matching this loop, so terminate loop */ \
199+
estate->exitlabel = NULL; \
200+
rc = PLPGSQL_RC_OK; \
201+
exit_action; \
202+
} \
203+
else \
204+
{ \
205+
/* non-matching labelled EXIT, propagate RC_EXIT out */ \
206+
exit_action; \
207+
} \
208+
} \
209+
else if (rc == PLPGSQL_RC_CONTINUE) \
210+
{ \
211+
if (estate->exitlabel == NULL) \
212+
{ \
213+
/* unlabelled CONTINUE matches this loop, so continue in loop */ \
214+
rc = PLPGSQL_RC_OK; \
215+
} \
216+
else if ((looplabel) != NULL && \
217+
strcmp(looplabel, estate->exitlabel) == 0) \
218+
{ \
219+
/* labelled CONTINUE matching this loop, so continue in loop */ \
220+
estate->exitlabel = NULL; \
221+
rc = PLPGSQL_RC_OK; \
222+
} \
223+
else \
224+
{ \
225+
/* non-matching labelled CONTINUE, propagate RC_CONTINUE out */ \
226+
exit_action; \
227+
} \
228+
} \
229+
else \
230+
Assert(rc == PLPGSQL_RC_OK)
231+
158232
/************************************************************
159233
* Local function forward declarations
160234
************************************************************/
@@ -1476,7 +1550,9 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
14761550
estate->err_text = NULL;
14771551

14781552
/*
1479-
* Handle the return code.
1553+
* Handle the return code. This is intentionally different from
1554+
* LOOP_RC_PROCESSING(): CONTINUE never matches a block, and EXIT matches
1555+
* a block only if there is a label match.
14801556
*/
14811557
switch (rc)
14821558
{
@@ -1486,11 +1562,6 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
14861562
return rc;
14871563

14881564
case PLPGSQL_RC_EXIT:
1489-
1490-
/*
1491-
* This is intentionally different from the handling of RC_EXIT
1492-
* for loops: to match a block, we require a match by label.
1493-
*/
14941565
if (estate->exitlabel == NULL)
14951566
return PLPGSQL_RC_EXIT;
14961567
if (block->label == NULL)
@@ -1948,45 +2019,16 @@ exec_stmt_case(PLpgSQL_execstate *estate, PLpgSQL_stmt_case *stmt)
19482019
static int
19492020
exec_stmt_loop(PLpgSQL_execstate *estate, PLpgSQL_stmt_loop *stmt)
19502021
{
2022+
int rc = PLPGSQL_RC_OK;
2023+
19512024
for (;;)
19522025
{
1953-
int rc = exec_stmts(estate, stmt->body);
1954-
1955-
switch (rc)
1956-
{
1957-
case PLPGSQL_RC_OK:
1958-
break;
1959-
1960-
case PLPGSQL_RC_EXIT:
1961-
if (estate->exitlabel == NULL)
1962-
return PLPGSQL_RC_OK;
1963-
if (stmt->label == NULL)
1964-
return PLPGSQL_RC_EXIT;
1965-
if (strcmp(stmt->label, estate->exitlabel) != 0)
1966-
return PLPGSQL_RC_EXIT;
1967-
estate->exitlabel = NULL;
1968-
return PLPGSQL_RC_OK;
1969-
1970-
case PLPGSQL_RC_CONTINUE:
1971-
if (estate->exitlabel == NULL)
1972-
/* anonymous continue, so re-run the loop */
1973-
break;
1974-
else if (stmt->label != NULL &&
1975-
strcmp(stmt->label, estate->exitlabel) == 0)
1976-
/* label matches named continue, so re-run loop */
1977-
estate->exitlabel = NULL;
1978-
else
1979-
/* label doesn't match named continue, so propagate upward */
1980-
return PLPGSQL_RC_CONTINUE;
1981-
break;
1982-
1983-
case PLPGSQL_RC_RETURN:
1984-
return rc;
2026+
rc = exec_stmts(estate, stmt->body);
19852027

1986-
default:
1987-
elog(ERROR, "unrecognized rc: %d", rc);
1988-
}
2028+
LOOP_RC_PROCESSING(stmt->label, break);
19892029
}
2030+
2031+
return rc;
19902032
}
19912033

19922034

@@ -1999,9 +2041,10 @@ exec_stmt_loop(PLpgSQL_execstate *estate, PLpgSQL_stmt_loop *stmt)
19992041
static int
20002042
exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt)
20012043
{
2044+
int rc = PLPGSQL_RC_OK;
2045+
20022046
for (;;)
20032047
{
2004-
int rc;
20052048
bool value;
20062049
bool isnull;
20072050

@@ -2013,43 +2056,10 @@ exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt)
20132056

20142057
rc = exec_stmts(estate, stmt->body);
20152058

2016-
switch (rc)
2017-
{
2018-
case PLPGSQL_RC_OK:
2019-
break;
2020-
2021-
case PLPGSQL_RC_EXIT:
2022-
if (estate->exitlabel == NULL)
2023-
return PLPGSQL_RC_OK;
2024-
if (stmt->label == NULL)
2025-
return PLPGSQL_RC_EXIT;
2026-
if (strcmp(stmt->label, estate->exitlabel) != 0)
2027-
return PLPGSQL_RC_EXIT;
2028-
estate->exitlabel = NULL;
2029-
return PLPGSQL_RC_OK;
2030-
2031-
case PLPGSQL_RC_CONTINUE:
2032-
if (estate->exitlabel == NULL)
2033-
/* anonymous continue, so re-run loop */
2034-
break;
2035-
else if (stmt->label != NULL &&
2036-
strcmp(stmt->label, estate->exitlabel) == 0)
2037-
/* label matches named continue, so re-run loop */
2038-
estate->exitlabel = NULL;
2039-
else
2040-
/* label doesn't match named continue, propagate upward */
2041-
return PLPGSQL_RC_CONTINUE;
2042-
break;
2043-
2044-
case PLPGSQL_RC_RETURN:
2045-
return rc;
2046-
2047-
default:
2048-
elog(ERROR, "unrecognized rc: %d", rc);
2049-
}
2059+
LOOP_RC_PROCESSING(stmt->label, break);
20502060
}
20512061

2052-
return PLPGSQL_RC_OK;
2062+
return rc;
20532063
}
20542064

20552065

@@ -2163,50 +2173,7 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt)
21632173
*/
21642174
rc = exec_stmts(estate, stmt->body);
21652175

2166-
if (rc == PLPGSQL_RC_RETURN)
2167-
break; /* break out of the loop */
2168-
else if (rc == PLPGSQL_RC_EXIT)
2169-
{
2170-
if (estate->exitlabel == NULL)
2171-
/* unlabelled exit, finish the current loop */
2172-
rc = PLPGSQL_RC_OK;
2173-
else if (stmt->label != NULL &&
2174-
strcmp(stmt->label, estate->exitlabel) == 0)
2175-
{
2176-
/* labelled exit, matches the current stmt's label */
2177-
estate->exitlabel = NULL;
2178-
rc = PLPGSQL_RC_OK;
2179-
}
2180-
2181-
/*
2182-
* otherwise, this is a labelled exit that does not match the
2183-
* current statement's label, if any: return RC_EXIT so that the
2184-
* EXIT continues to propagate up the stack.
2185-
*/
2186-
break;
2187-
}
2188-
else if (rc == PLPGSQL_RC_CONTINUE)
2189-
{
2190-
if (estate->exitlabel == NULL)
2191-
/* unlabelled continue, so re-run the current loop */
2192-
rc = PLPGSQL_RC_OK;
2193-
else if (stmt->label != NULL &&
2194-
strcmp(stmt->label, estate->exitlabel) == 0)
2195-
{
2196-
/* label matches named continue, so re-run loop */
2197-
estate->exitlabel = NULL;
2198-
rc = PLPGSQL_RC_OK;
2199-
}
2200-
else
2201-
{
2202-
/*
2203-
* otherwise, this is a named continue that does not match the
2204-
* current statement's label, if any: return RC_CONTINUE so
2205-
* that the CONTINUE will propagate up the stack.
2206-
*/
2207-
break;
2208-
}
2209-
}
2176+
LOOP_RC_PROCESSING(stmt->label, break);
22102177

22112178
/*
22122179
* Increase/decrease loop value, unless it would overflow, in which
@@ -2536,51 +2503,7 @@ exec_stmt_foreach_a(PLpgSQL_execstate *estate, PLpgSQL_stmt_foreach_a *stmt)
25362503
*/
25372504
rc = exec_stmts(estate, stmt->body);
25382505

2539-
/* Handle the return code */
2540-
if (rc == PLPGSQL_RC_RETURN)
2541-
break; /* break out of the loop */
2542-
else if (rc == PLPGSQL_RC_EXIT)
2543-
{
2544-
if (estate->exitlabel == NULL)
2545-
/* unlabelled exit, finish the current loop */
2546-
rc = PLPGSQL_RC_OK;
2547-
else if (stmt->label != NULL &&
2548-
strcmp(stmt->label, estate->exitlabel) == 0)
2549-
{
2550-
/* labelled exit, matches the current stmt's label */
2551-
estate->exitlabel = NULL;
2552-
rc = PLPGSQL_RC_OK;
2553-
}
2554-
2555-
/*
2556-
* otherwise, this is a labelled exit that does not match the
2557-
* current statement's label, if any: return RC_EXIT so that the
2558-
* EXIT continues to propagate up the stack.
2559-
*/
2560-
break;
2561-
}
2562-
else if (rc == PLPGSQL_RC_CONTINUE)
2563-
{
2564-
if (estate->exitlabel == NULL)
2565-
/* unlabelled continue, so re-run the current loop */
2566-
rc = PLPGSQL_RC_OK;
2567-
else if (stmt->label != NULL &&
2568-
strcmp(stmt->label, estate->exitlabel) == 0)
2569-
{
2570-
/* label matches named continue, so re-run loop */
2571-
estate->exitlabel = NULL;
2572-
rc = PLPGSQL_RC_OK;
2573-
}
2574-
else
2575-
{
2576-
/*
2577-
* otherwise, this is a named continue that does not match the
2578-
* current statement's label, if any: return RC_CONTINUE so
2579-
* that the CONTINUE will propagate up the stack.
2580-
*/
2581-
break;
2582-
}
2583-
}
2506+
LOOP_RC_PROCESSING(stmt->label, break);
25842507

25852508
MemoryContextSwitchTo(stmt_mcontext);
25862509
}
@@ -5381,60 +5304,7 @@ exec_for_query(PLpgSQL_execstate *estate, PLpgSQL_stmt_forq *stmt,
53815304
*/
53825305
rc = exec_stmts(estate, stmt->body);
53835306

5384-
if (rc != PLPGSQL_RC_OK)
5385-
{
5386-
if (rc == PLPGSQL_RC_EXIT)
5387-
{
5388-
if (estate->exitlabel == NULL)
5389-
{
5390-
/* unlabelled exit, so exit the current loop */
5391-
rc = PLPGSQL_RC_OK;
5392-
}
5393-
else if (stmt->label != NULL &&
5394-
strcmp(stmt->label, estate->exitlabel) == 0)
5395-
{
5396-
/* label matches this loop, so exit loop */
5397-
estate->exitlabel = NULL;
5398-
rc = PLPGSQL_RC_OK;
5399-
}
5400-
5401-
/*
5402-
* otherwise, we processed a labelled exit that does not
5403-
* match the current statement's label, if any; return
5404-
* RC_EXIT so that the EXIT continues to recurse upward.
5405-
*/
5406-
}
5407-
else if (rc == PLPGSQL_RC_CONTINUE)
5408-
{
5409-
if (estate->exitlabel == NULL)
5410-
{
5411-
/* unlabelled continue, so re-run the current loop */
5412-
rc = PLPGSQL_RC_OK;
5413-
continue;
5414-
}
5415-
else if (stmt->label != NULL &&
5416-
strcmp(stmt->label, estate->exitlabel) == 0)
5417-
{
5418-
/* label matches this loop, so re-run loop */
5419-
estate->exitlabel = NULL;
5420-
rc = PLPGSQL_RC_OK;
5421-
continue;
5422-
}
5423-
5424-
/*
5425-
* otherwise, we process a labelled continue that does not
5426-
* match the current statement's label, if any; return
5427-
* RC_CONTINUE so that the CONTINUE will propagate up the
5428-
* stack.
5429-
*/
5430-
}
5431-
5432-
/*
5433-
* We're aborting the loop. Need a goto to get out of two
5434-
* levels of loop...
5435-
*/
5436-
goto loop_exit;
5437-
}
5307+
LOOP_RC_PROCESSING(stmt->label, goto loop_exit);
54385308
}
54395309

54405310
SPI_freetuptable(tuptab);

0 commit comments

Comments
 (0)