patch: remove redundant code from pl_exec.c

Started by Pavel Stehuleabout 15 years ago11 messages
#1Pavel Stehule
pavel.stehule@gmail.com
1 attachment(s)

Hello

This patch remove redundant rows from PL/pgSQL executor (-89 lines).
Doesn't change a functionality.

Regards

Pavel Stehule

Attachments:

plpgsql-reduce-redundant-code.difftext/x-patch; charset=US-ASCII; name=plpgsql-reduce-redundant-code.diffDownload
*** ./src/pl/plpgsql/src/pl_exec.c.orig	2010-12-16 10:25:37.000000000 +0100
--- ./src/pl/plpgsql/src/pl_exec.c	2010-12-17 10:50:31.793623763 +0100
***************
*** 204,209 ****
--- 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 ****
--- 1568,1637 ----
  	return exec_stmts(estate, stmt->else_stmts);
  }
  
+ /*
+  * function returns true, when outer cycle should be leaved
+  */
+ static bool 
+ can_leave_loop(PLpgSQL_execstate *estate, PLpgSQL_any_loop *stmt, int *rc)
+ {
+ 	switch (*rc)
+ 	{
+ 		case PLPGSQL_RC_OK:
+ 			return false;
+ 
+ 		case PLPGSQL_RC_RETURN:
+ 			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;		/* be compiler quiet */
+ 	}
+ }
  
  /* ----------
   * exec_stmt_loop			Loop over statements until
***************
*** 1575,1621 ****
  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;
  }
  
  
--- 1641,1657 ----
  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))
! 			break;
  	}
  
! 	return rc;
  }
  
  
***************
*** 1628,1636 ****
  static int
  exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt)
  {
  	for (;;)
  	{
- 		int			rc;
  		bool		value;
  		bool		isnull;
  
--- 1664,1673 ----
  static int
  exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt)
  {
+ 	int	rc;
+ 
  	for (;;)
  	{
  		bool		value;
  		bool		isnull;
  
***************
*** 1642,1684 ****
  
  		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;
  }
  
  
--- 1679,1689 ----
  
  		rc = exec_stmts(estate, stmt->body);
  
! 		if (can_leave_loop(estate, (PLpgSQL_any_loop *) stmt, &rc))
! 			break;
  	}
  
! 	return rc;
  }
  
  
***************
*** 1789,1838 ****
  		 */
  		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
--- 1794,1801 ----
  		 */
  		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 ****
  			 */
  			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);
--- 4373,4380 ----
  			 */
  			rc = exec_stmts(estate, stmt->body);
  
! 			if (can_leave_loop(estate, (PLpgSQL_any_loop *) stmt, &rc))
  				goto loop_exit;
  		}
  
  		SPI_freetuptable(tuptab);
*** ./src/pl/plpgsql/src/plpgsql.h.orig	2010-12-16 09:14:42.000000000 +0100
--- ./src/pl/plpgsql/src/plpgsql.h	2010-12-17 09:54:39.726801715 +0100
***************
*** 407,412 ****
--- 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;
#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: Pavel Stehule (#1)
Re: patch: remove redundant code from pl_exec.c

Pavel Stehule <pavel.stehule@gmail.com> writes:

This patch remove redundant rows from PL/pgSQL executor (-89 lines).
Doesn't change a functionality.

I'm not really impressed with this idea: there's no a priori reason
that all those loop types would necessarily have exactly the same
control logic.

regards, tom lane

#3Alvaro Herrera
alvherre@commandprompt.com
In reply to: Pavel Stehule (#1)
Re: patch: remove redundant code from pl_exec.c

Excerpts from Pavel Stehule's message of vie dic 17 07:02:00 -0300 2010:

Hello

This patch remove redundant rows from PL/pgSQL executor (-89 lines).
Doesn't change a functionality.

Hmm I'm not sure but I think the new code has some of the result values
inverted. Did you test this thoroughly? I think this would be a nice
simplification because the repetitive coding is ugly and confusing, but
I'm nervous about the unstated assumption that all loop structs are
castable to the new struct. Seems like it could be easily broken in the
future.

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#4Pavel Stehule
pavel.stehule@gmail.com
In reply to: Tom Lane (#2)
Re: patch: remove redundant code from pl_exec.c

2010/12/17 Tom Lane <tgl@sss.pgh.pa.us>:

Pavel Stehule <pavel.stehule@gmail.com> writes:

This patch remove redundant rows from PL/pgSQL executor (-89 lines).
Doesn't change a functionality.

I'm not really impressed with this idea: there's no a priori reason
that all those loop types would necessarily have exactly the same
control logic.

this code processes a rc from EXIT, CONTINUE and RETURN statement. All
these statements are defined independent to outer loops, so there are
no reason why this code has be different. And actually removed code
was almost same. There was different a process for FOR statement,
because there isn't possible direct "return" from cycle, because is
necessary to release a allocated memory.

There is no reason why the processing should be same, but actually is same.

Show quoted text

                       regards, tom lane

#5Pavel Stehule
pavel.stehule@gmail.com
In reply to: Alvaro Herrera (#3)
Re: patch: remove redundant code from pl_exec.c

2010/12/17 Alvaro Herrera <alvherre@commandprompt.com>:

Excerpts from Pavel Stehule's message of vie dic 17 07:02:00 -0300 2010:

Hello

This patch remove redundant rows from PL/pgSQL executor (-89 lines).
Doesn't change a functionality.

Hmm I'm not sure but I think the new code has some of the result values
inverted.  Did you test this thoroughly?  I think this would be a nice
simplification because the repetitive coding is ugly and confusing, but
I'm nervous about the unstated assumption that all loop structs are
castable to the new struct.  Seems like it could be easily broken in the
future.

All regress tests was successful.

A common structure isn't a new. There is same for FOR loops, there is
some similar in parser yylval, and it is only explicit description of
used construction for stmt structures. I should not to modify any
other structure. But I am not strong in this. A interface can be
changed and enhanced about pointer to label. Just I am not satisfied
from current state, where same things are implemented with minimal
difference.

Pavel

Show quoted text

--
Álvaro Herrera <alvherre@commandprompt.com>
The PostgreSQL Company - Command Prompt, Inc.
PostgreSQL Replication, Consulting, Custom Development, 24x7 support

#6Tom Lane
tgl@sss.pgh.pa.us
In reply to: Pavel Stehule (#4)
Re: patch: remove redundant code from pl_exec.c

Pavel Stehule <pavel.stehule@gmail.com> writes:

2010/12/17 Tom Lane <tgl@sss.pgh.pa.us>:

I'm not really impressed with this idea: there's no a priori reason
that all those loop types would necessarily have exactly the same
control logic.

There is no reason why the processing should be same, but actually is same.

Yes, and it might need to be different in future, whereupon this patch
would make life extremely difficult.

regards, tom lane

#7Pavel Stehule
pavel.stehule@gmail.com
In reply to: Tom Lane (#6)
Re: patch: remove redundant code from pl_exec.c

2010/12/17 Tom Lane <tgl@sss.pgh.pa.us>:

Pavel Stehule <pavel.stehule@gmail.com> writes:

2010/12/17 Tom Lane <tgl@sss.pgh.pa.us>:

I'm not really impressed with this idea: there's no a priori reason
that all those loop types would necessarily have exactly the same
control logic.

There is no reason why the processing should be same, but actually is same.

Yes, and it might need to be different in future, whereupon this patch
would make life extremely difficult.

Do you know about some possible change?

I can't to argument with this argument. But I can use a same argument.
Isn't be more practical to have a centralized management for return
code? There is same probability to be some features in future that
will need a modify this fragment - for example a new return code
value. Without centralized management, you will have to modify four
fragments instead one.

Regards

Pavel Stehule

Show quoted text

                       regards, tom lane

#8Stephen Frost
sfrost@snowman.net
In reply to: Pavel Stehule (#1)
1 attachment(s)
REVIEW: patch: remove redundant code from pl_exec.c

Greetings,

* Pavel Stehule (pavel.stehule@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 <sfrost@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 <sfrost@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

Attachments:

plpgsql_change.patchtext/x-diff; charset=us-asciiDownload
*** 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;
#9Tom Lane
tgl@sss.pgh.pa.us
In reply to: Stephen Frost (#8)
Re: REVIEW: patch: remove redundant code from pl_exec.c

Stephen Frost <sfrost@snowman.net> writes:

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.

That was my reaction as well; and I was also concerned that this could
have a non-negligible performance price. (At the very least it's adding
an additional function call per loop execution, and there could also be
a penalty from forcing "rc" to be in memory rather than a register.)

I think we should reject this one.

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.

Using a switch there is a bit problematic since in some cases you want
to use "break" to exit the loop. We could replace such breaks by gotos,
but that would be another strike against the argument that you're making
things more readable. I think the switch in exec_stmt_loop is only
workable because it has no cleanup to do, so it can just "return" in
places where a loop break would otherwise be needed. In short: if you
want to make these all look alike, better to go the other way.

regards, tom lane

#10Stephen Frost
sfrost@snowman.net
In reply to: Tom Lane (#9)
Re: REVIEW: patch: remove redundant code from pl_exec.c

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

I think we should reject this one.

Works for me.

Using a switch there is a bit problematic since in some cases you want
to use "break" to exit the loop. We could replace such breaks by gotos,
but that would be another strike against the argument that you're making
things more readable. I think the switch in exec_stmt_loop is only
workable because it has no cleanup to do, so it can just "return" in
places where a loop break would otherwise be needed. In short: if you
want to make these all look alike, better to go the other way.

Ah, yeah, good point. We do use gotos elsewhere for this reason, might
consider revisiting those also, if we're trying to a 'clean-up' patch.
In any case, I'll mark this as rejected.

Thanks!

Stephen

#11Pavel Stehule
pavel.stehule@gmail.com
In reply to: Stephen Frost (#10)
Re: REVIEW: patch: remove redundant code from pl_exec.c

2011/1/19 Stephen Frost <sfrost@snowman.net>:

* Tom Lane (tgl@sss.pgh.pa.us) wrote:

I think we should reject this one.

Works for me.

Using a switch there is a bit problematic since in some cases you want
to use "break" to exit the loop.  We could replace such breaks by gotos,
but that would be another strike against the argument that you're making
things more readable.  I think the switch in exec_stmt_loop is only
workable because it has no cleanup to do, so it can just "return" in
places where a loop break would otherwise be needed.  In short: if you
want to make these all look alike, better to go the other way.

Ah, yeah, good point.  We do use gotos elsewhere for this reason, might
consider revisiting those also, if we're trying to a 'clean-up' patch.
In any case, I'll mark this as rejected.

ok, I don't thinking so current code is readable, but I can't to do better now.

Thank you for review.

Regards

Pavel Stehule

Show quoted text

       Thanks!

               Stephen

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)

iEYEARECAAYFAk03S10ACgkQrzgMPqB3kigWdQCeIU/dvgJ8bMVZ7nh+TYiFHVlP
4H4AnR/JbboMWbCu95G2aUEcP3LZDDGM
=R8c6
-----END PGP SIGNATURE-----