mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-31 00:03:57 -04:00 
			
		
		
		
	Add a CONTINUE statement to PL/PgSQL, which can be used to begin the
next iteration of a loop. Update documentation and add regression tests. Patch from Pavel Stehule, reviewed by Neil Conway.
This commit is contained in:
		
							parent
							
								
									7a28de2052
								
							
						
					
					
						commit
						ebcb4c931d
					
				| @ -1,5 +1,5 @@ | |||||||
| <!-- | <!-- | ||||||
| $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.73 2005/06/19 23:39:05 neilc Exp $ | $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.74 2005/06/22 01:35:02 neilc Exp $ | ||||||
| --> | --> | ||||||
| 
 | 
 | ||||||
| <chapter id="plpgsql">  | <chapter id="plpgsql">  | ||||||
| @ -1779,10 +1779,10 @@ END IF; | |||||||
|     </indexterm> |     </indexterm> | ||||||
| 
 | 
 | ||||||
|     <para> |     <para> | ||||||
|      With the <literal>LOOP</>, <literal>EXIT</>, <literal>WHILE</>, |      With the <literal>LOOP</>, <literal>EXIT</>, | ||||||
|      and <literal>FOR</> statements, you can arrange for your |      <literal>CONTINUE</>, <literal>WHILE</>, and <literal>FOR</> | ||||||
|      <application>PL/pgSQL</application> function to repeat a series |      statements, you can arrange for your <application>PL/pgSQL</> | ||||||
|      of commands. |      function to repeat a series of commands. | ||||||
|     </para> |     </para> | ||||||
| 
 | 
 | ||||||
|     <sect3> |     <sect3> | ||||||
| @ -1807,30 +1807,36 @@ END LOOP; | |||||||
|      <sect3> |      <sect3> | ||||||
|       <title><literal>EXIT</></title> |       <title><literal>EXIT</></title> | ||||||
| 
 | 
 | ||||||
|  |      <indexterm> | ||||||
|  |       <primary>EXIT</primary> | ||||||
|  |       <secondary>in PL/pgSQL</secondary> | ||||||
|  |      </indexterm> | ||||||
|  | 
 | ||||||
| <synopsis> | <synopsis> | ||||||
| EXIT <optional> <replaceable>label</replaceable> </optional> <optional> WHEN <replaceable>expression</replaceable> </optional>; | EXIT <optional> <replaceable>label</replaceable> </optional> <optional> WHEN <replaceable>expression</replaceable> </optional>; | ||||||
| </synopsis> | </synopsis> | ||||||
| 
 | 
 | ||||||
|        <para> |        <para> | ||||||
|         If no <replaceable>label</replaceable> is given, |         If no <replaceable>label</replaceable> is given, the innermost | ||||||
|         the innermost loop is terminated and the |         loop is terminated and the statement following <literal>END | ||||||
|         statement following <literal>END LOOP</> is executed next. |         LOOP</> is executed next.  If <replaceable>label</replaceable> | ||||||
|         If <replaceable>label</replaceable> is given, it |         is given, it must be the label of the current or some outer | ||||||
|         must be the label of the current or some outer level of nested loop |         level of nested loop or block. Then the named loop or block is | ||||||
|         or block. Then the named loop or block is terminated and control |         terminated and control continues with the statement after the | ||||||
|         continues with the statement after the loop's/block's corresponding |         loop's/block's corresponding <literal>END</>. | ||||||
|         <literal>END</>. |  | ||||||
|        </para> |        </para> | ||||||
| 
 | 
 | ||||||
|        <para> |        <para> | ||||||
|         If <literal>WHEN</> is present, loop exit occurs only if the specified |         If <literal>WHEN</> is specified, the loop exit occurs only if | ||||||
|         condition is true, otherwise control passes to the statement after |         <replaceable>expression</> is true. Otherwise, control passes | ||||||
|         <literal>EXIT</>. |         to the statement after <literal>EXIT</>. | ||||||
|        </para> |        </para> | ||||||
| 
 | 
 | ||||||
|        <para> |        <para> | ||||||
|         <literal>EXIT</> can be used to cause early exit from all types of |         <literal>EXIT</> can be used with all types of loops; it is | ||||||
|         loops; it is not limited to use with unconditional loops. |         not limited to use with unconditional loops. When used with a | ||||||
|  |         <literal>BEGIN</literal> block, <literal>EXIT</literal> passes | ||||||
|  |         control to the next statement after the end of the block. | ||||||
|        </para> |        </para> | ||||||
| 
 | 
 | ||||||
|        <para> |        <para> | ||||||
| @ -1858,9 +1864,61 @@ END; | |||||||
|        </para> |        </para> | ||||||
|      </sect3> |      </sect3> | ||||||
| 
 | 
 | ||||||
|  |      <sect3> | ||||||
|  |       <title><literal>CONTINUE</></title> | ||||||
|  | 
 | ||||||
|  |      <indexterm> | ||||||
|  |       <primary>CONTINUE</primary> | ||||||
|  |       <secondary>in PL/pgSQL</secondary> | ||||||
|  |      </indexterm> | ||||||
|  | 
 | ||||||
|  | <synopsis> | ||||||
|  | CONTINUE <optional> <replaceable>label</replaceable> </optional> <optional> WHEN <replaceable>expression</replaceable> </optional>; | ||||||
|  | </synopsis> | ||||||
|  | 
 | ||||||
|  |        <para> | ||||||
|  |         If no <replaceable>label</> is given, the next iteration of | ||||||
|  |         the innermost loop is begun. That is, control is passed back | ||||||
|  |         to the loop control expression (if any), and the body of the | ||||||
|  |         loop is re-evaluated. If <replaceable>label</> is present, it | ||||||
|  |         specifies the label of the loop whose execution will be | ||||||
|  |         continued. | ||||||
|  |        </para> | ||||||
|  | 
 | ||||||
|  |        <para> | ||||||
|  |         If <literal>WHEN</> is specified, the next iteration of the | ||||||
|  |         loop is begun only if <replaceable>expression</> is | ||||||
|  |         true. Otherwise, control passes to the statement after | ||||||
|  |         <literal>CONTINUE</>. | ||||||
|  |        </para> | ||||||
|  | 
 | ||||||
|  |        <para> | ||||||
|  |         <literal>CONTINUE</> can be used with all types of loops; it | ||||||
|  |         is not limited to use with unconditional loops. | ||||||
|  |        </para> | ||||||
|  | 
 | ||||||
|  |        <para> | ||||||
|  |         Examples: | ||||||
|  | <programlisting> | ||||||
|  | LOOP | ||||||
|  |     -- some computations | ||||||
|  |     EXIT WHEN count > 100; | ||||||
|  |     CONTINUE WHEN count < 50; | ||||||
|  |     -- some computations for count IN [50 .. 100]  | ||||||
|  | END LOOP; | ||||||
|  | </programlisting> | ||||||
|  |        </para> | ||||||
|  |      </sect3> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|      <sect3> |      <sect3> | ||||||
|       <title><literal>WHILE</></title> |       <title><literal>WHILE</></title> | ||||||
| 
 | 
 | ||||||
|  |      <indexterm> | ||||||
|  |       <primary>WHILE</primary> | ||||||
|  |       <secondary>in PL/pgSQL</secondary> | ||||||
|  |      </indexterm> | ||||||
|  | 
 | ||||||
| <synopsis> | <synopsis> | ||||||
| <optional><<<replaceable>label</replaceable>>></optional> | <optional><<<replaceable>label</replaceable>>></optional> | ||||||
| WHILE <replaceable>expression</replaceable> LOOP | WHILE <replaceable>expression</replaceable> LOOP | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ | |||||||
|  *						  procedural language |  *						  procedural language | ||||||
|  * |  * | ||||||
|  * IDENTIFICATION |  * IDENTIFICATION | ||||||
|  *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.76 2005/06/14 06:43:14 neilc Exp $ |  *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.77 2005/06/22 01:35:02 neilc Exp $ | ||||||
|  * |  * | ||||||
|  *	  This software is copyrighted by Jan Wieck - Hamburg. |  *	  This software is copyrighted by Jan Wieck - Hamburg. | ||||||
|  * |  * | ||||||
| @ -61,6 +61,7 @@ static	void			 plpgsql_sql_error_callback(void *arg); | |||||||
| 
 | 
 | ||||||
| %union { | %union { | ||||||
| 		int32					ival; | 		int32					ival; | ||||||
|  | 		bool					boolean; | ||||||
| 		char					*str; | 		char					*str; | ||||||
| 		struct | 		struct | ||||||
| 		{ | 		{ | ||||||
| @ -100,7 +101,7 @@ static	void			 plpgsql_sql_error_callback(void *arg); | |||||||
| %type <declhdr> decl_sect | %type <declhdr> decl_sect | ||||||
| %type <varname> decl_varname | %type <varname> decl_varname | ||||||
| %type <str>		decl_renname | %type <str>		decl_renname | ||||||
| %type <ival>	decl_const decl_notnull | %type <boolean>	decl_const decl_notnull exit_type | ||||||
| %type <expr>	decl_defval decl_cursor_query | %type <expr>	decl_defval decl_cursor_query | ||||||
| %type <dtype>	decl_datatype | %type <dtype>	decl_datatype | ||||||
| %type <row>		decl_cursor_args | %type <row>		decl_cursor_args | ||||||
| @ -153,6 +154,7 @@ static	void			 plpgsql_sql_error_callback(void *arg); | |||||||
| %token	K_BEGIN | %token	K_BEGIN | ||||||
| %token	K_CLOSE | %token	K_CLOSE | ||||||
| %token	K_CONSTANT | %token	K_CONSTANT | ||||||
|  | %token	K_CONTINUE | ||||||
| %token	K_CURSOR | %token	K_CURSOR | ||||||
| %token	K_DEBUG | %token	K_DEBUG | ||||||
| %token	K_DECLARE | %token	K_DECLARE | ||||||
| @ -514,9 +516,9 @@ decl_renname	: T_WORD | |||||||
| 				; | 				; | ||||||
| 
 | 
 | ||||||
| decl_const		: | decl_const		: | ||||||
| 					{ $$ = 0; } | 					{ $$ = false; } | ||||||
| 				| K_CONSTANT | 				| K_CONSTANT | ||||||
| 					{ $$ = 1; } | 					{ $$ = true; } | ||||||
| 				; | 				; | ||||||
| 
 | 
 | ||||||
| decl_datatype	: | decl_datatype	: | ||||||
| @ -531,9 +533,9 @@ decl_datatype	: | |||||||
| 				; | 				; | ||||||
| 
 | 
 | ||||||
| decl_notnull	: | decl_notnull	: | ||||||
| 					{ $$ = 0; } | 					{ $$ = false; } | ||||||
| 				| K_NOT K_NULL | 				| K_NOT K_NULL | ||||||
| 					{ $$ = 1; } | 					{ $$ = true; } | ||||||
| 				; | 				; | ||||||
| 
 | 
 | ||||||
| decl_defval		: ';' | decl_defval		: ';' | ||||||
| @ -1035,12 +1037,13 @@ stmt_select		: K_SELECT lno | |||||||
| 					} | 					} | ||||||
| 				; | 				; | ||||||
| 
 | 
 | ||||||
| stmt_exit		: K_EXIT lno opt_exitlabel opt_exitcond | stmt_exit		: exit_type lno opt_exitlabel opt_exitcond | ||||||
| 					{ | 					{ | ||||||
| 						PLpgSQL_stmt_exit *new; | 						PLpgSQL_stmt_exit *new; | ||||||
| 
 | 
 | ||||||
| 						new = palloc0(sizeof(PLpgSQL_stmt_exit)); | 						new = palloc0(sizeof(PLpgSQL_stmt_exit)); | ||||||
| 						new->cmd_type = PLPGSQL_STMT_EXIT; | 						new->cmd_type = PLPGSQL_STMT_EXIT; | ||||||
|  | 						new->is_exit  = $1; | ||||||
| 						new->lineno	  = $2; | 						new->lineno	  = $2; | ||||||
| 						new->label	  = $3; | 						new->label	  = $3; | ||||||
| 						new->cond	  = $4; | 						new->cond	  = $4; | ||||||
| @ -1049,6 +1052,16 @@ stmt_exit		: K_EXIT lno opt_exitlabel opt_exitcond | |||||||
| 					} | 					} | ||||||
| 				; | 				; | ||||||
| 
 | 
 | ||||||
|  | exit_type		: K_EXIT | ||||||
|  | 					{ | ||||||
|  | 						$$ = true; | ||||||
|  | 					} | ||||||
|  | 				| K_CONTINUE | ||||||
|  | 					{ | ||||||
|  | 						$$ = false; | ||||||
|  | 					} | ||||||
|  | 				; | ||||||
|  | 
 | ||||||
| stmt_return		: K_RETURN lno | stmt_return		: K_RETURN lno | ||||||
| 					{ | 					{ | ||||||
| 						PLpgSQL_stmt_return *new; | 						PLpgSQL_stmt_return *new; | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ | |||||||
|  *			  procedural language |  *			  procedural language | ||||||
|  * |  * | ||||||
|  * IDENTIFICATION |  * IDENTIFICATION | ||||||
|  *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.146 2005/06/20 22:51:29 tgl Exp $ |  *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.147 2005/06/22 01:35:02 neilc Exp $ | ||||||
|  * |  * | ||||||
|  *	  This software is copyrighted by Jan Wieck - Hamburg. |  *	  This software is copyrighted by Jan Wieck - Hamburg. | ||||||
|  * |  * | ||||||
| @ -194,6 +194,7 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo) | |||||||
| 	PLpgSQL_execstate estate; | 	PLpgSQL_execstate estate; | ||||||
| 	ErrorContextCallback plerrcontext; | 	ErrorContextCallback plerrcontext; | ||||||
| 	int			i; | 	int			i; | ||||||
|  | 	int			rc; | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Setup the execution state | 	 * Setup the execution state | ||||||
| @ -282,10 +283,21 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo) | |||||||
| 	 */ | 	 */ | ||||||
| 	estate.err_text = NULL; | 	estate.err_text = NULL; | ||||||
| 	estate.err_stmt = (PLpgSQL_stmt *) (func->action); | 	estate.err_stmt = (PLpgSQL_stmt *) (func->action); | ||||||
| 	if (exec_stmt_block(&estate, func->action) != PLPGSQL_RC_RETURN) | 	rc = exec_stmt_block(&estate, func->action); | ||||||
|  | 	if (rc != PLPGSQL_RC_RETURN) | ||||||
| 	{ | 	{ | ||||||
| 		estate.err_stmt = NULL; | 		estate.err_stmt = NULL; | ||||||
| 		estate.err_text = NULL; | 		estate.err_text = NULL; | ||||||
|  | 
 | ||||||
|  | 		/*
 | ||||||
|  | 		 * Provide a more helpful message if a CONTINUE has been used | ||||||
|  | 		 * outside a loop. | ||||||
|  | 		 */ | ||||||
|  | 		if (rc == PLPGSQL_RC_CONTINUE) | ||||||
|  | 			ereport(ERROR, | ||||||
|  | 					(errcode(ERRCODE_SYNTAX_ERROR), | ||||||
|  | 					 errmsg("CONTINUE cannot be used outside a loop"))); | ||||||
|  | 		else | ||||||
| 			ereport(ERROR, | 			ereport(ERROR, | ||||||
| 					(errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT), | 					(errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT), | ||||||
| 					 errmsg("control reached end of function without RETURN"))); | 					 errmsg("control reached end of function without RETURN"))); | ||||||
| @ -393,6 +405,7 @@ plpgsql_exec_trigger(PLpgSQL_function *func, | |||||||
| 	PLpgSQL_execstate estate; | 	PLpgSQL_execstate estate; | ||||||
| 	ErrorContextCallback plerrcontext; | 	ErrorContextCallback plerrcontext; | ||||||
| 	int			i; | 	int			i; | ||||||
|  | 	int			rc; | ||||||
| 	PLpgSQL_var *var; | 	PLpgSQL_var *var; | ||||||
| 	PLpgSQL_rec *rec_new, | 	PLpgSQL_rec *rec_new, | ||||||
| 			   *rec_old; | 			   *rec_old; | ||||||
| @ -546,10 +559,21 @@ plpgsql_exec_trigger(PLpgSQL_function *func, | |||||||
| 	 */ | 	 */ | ||||||
| 	estate.err_text = NULL; | 	estate.err_text = NULL; | ||||||
| 	estate.err_stmt = (PLpgSQL_stmt *) (func->action); | 	estate.err_stmt = (PLpgSQL_stmt *) (func->action); | ||||||
| 	if (exec_stmt_block(&estate, func->action) != PLPGSQL_RC_RETURN) | 	rc = exec_stmt_block(&estate, func->action); | ||||||
|  | 	if (rc != PLPGSQL_RC_RETURN) | ||||||
| 	{ | 	{ | ||||||
| 		estate.err_stmt = NULL; | 		estate.err_stmt = NULL; | ||||||
| 		estate.err_text = NULL; | 		estate.err_text = NULL; | ||||||
|  | 
 | ||||||
|  | 		/*
 | ||||||
|  | 		 * Provide a more helpful message if a CONTINUE has been used | ||||||
|  | 		 * outside a loop. | ||||||
|  | 		 */ | ||||||
|  | 		if (rc == PLPGSQL_RC_CONTINUE) | ||||||
|  | 			ereport(ERROR, | ||||||
|  | 					(errcode(ERRCODE_SYNTAX_ERROR), | ||||||
|  | 					 errmsg("CONTINUE cannot be used outside a loop"))); | ||||||
|  | 		else | ||||||
| 			ereport(ERROR, | 			ereport(ERROR, | ||||||
| 					(errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT), | 					(errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT), | ||||||
| 					 errmsg("control reached end of trigger procedure without RETURN"))); | 					 errmsg("control reached end of trigger procedure without RETURN"))); | ||||||
| @ -919,7 +943,9 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block) | |||||||
| 	switch (rc) | 	switch (rc) | ||||||
| 	{ | 	{ | ||||||
| 		case PLPGSQL_RC_OK: | 		case PLPGSQL_RC_OK: | ||||||
| 			return PLPGSQL_RC_OK; | 		case PLPGSQL_RC_CONTINUE: | ||||||
|  | 		case PLPGSQL_RC_RETURN: | ||||||
|  | 			return rc; | ||||||
| 
 | 
 | ||||||
| 		case PLPGSQL_RC_EXIT: | 		case PLPGSQL_RC_EXIT: | ||||||
| 			if (estate->exitlabel == NULL) | 			if (estate->exitlabel == NULL) | ||||||
| @ -931,9 +957,6 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block) | |||||||
| 			estate->exitlabel = NULL; | 			estate->exitlabel = NULL; | ||||||
| 			return PLPGSQL_RC_OK; | 			return PLPGSQL_RC_OK; | ||||||
| 		 | 		 | ||||||
| 		case PLPGSQL_RC_RETURN: |  | ||||||
| 			return PLPGSQL_RC_RETURN; |  | ||||||
| 
 |  | ||||||
| 		default: | 		default: | ||||||
| 			elog(ERROR, "unrecognized rc: %d", rc); | 			elog(ERROR, "unrecognized rc: %d", rc); | ||||||
| 	} | 	} | ||||||
| @ -1120,7 +1143,7 @@ exec_stmt_getdiag(PLpgSQL_execstate *estate, PLpgSQL_stmt_getdiag *stmt) | |||||||
| 	{ | 	{ | ||||||
| 		PLpgSQL_diag_item	*diag_item = (PLpgSQL_diag_item *) lfirst(lc); | 		PLpgSQL_diag_item	*diag_item = (PLpgSQL_diag_item *) lfirst(lc); | ||||||
| 		PLpgSQL_datum		*var; | 		PLpgSQL_datum		*var; | ||||||
| 		bool				 isnull = false; | 		bool				 isnull; | ||||||
| 
 | 
 | ||||||
| 		if (diag_item->target <= 0) | 		if (diag_item->target <= 0) | ||||||
| 			continue; | 			continue; | ||||||
| @ -1165,7 +1188,7 @@ static int | |||||||
| exec_stmt_if(PLpgSQL_execstate *estate, PLpgSQL_stmt_if *stmt) | exec_stmt_if(PLpgSQL_execstate *estate, PLpgSQL_stmt_if *stmt) | ||||||
| { | { | ||||||
| 	bool		value; | 	bool		value; | ||||||
| 	bool		isnull = false; | 	bool		isnull; | ||||||
| 
 | 
 | ||||||
| 	value = exec_eval_boolean(estate, stmt->cond, &isnull); | 	value = exec_eval_boolean(estate, stmt->cond, &isnull); | ||||||
| 	exec_eval_cleanup(estate); | 	exec_eval_cleanup(estate); | ||||||
| @ -1209,11 +1232,24 @@ exec_stmt_loop(PLpgSQL_execstate *estate, PLpgSQL_stmt_loop *stmt) | |||||||
| 					return PLPGSQL_RC_OK; | 					return PLPGSQL_RC_OK; | ||||||
| 				if (stmt->label == NULL) | 				if (stmt->label == NULL) | ||||||
| 					return PLPGSQL_RC_EXIT; | 					return PLPGSQL_RC_EXIT; | ||||||
| 				if (strcmp(stmt->label, estate->exitlabel)) | 				if (strcmp(stmt->label, estate->exitlabel) != 0) | ||||||
| 					return PLPGSQL_RC_EXIT; | 					return PLPGSQL_RC_EXIT; | ||||||
| 				estate->exitlabel = NULL; | 				estate->exitlabel = NULL; | ||||||
| 				return PLPGSQL_RC_OK; | 				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: | 			case PLPGSQL_RC_RETURN: | ||||||
| 				return PLPGSQL_RC_RETURN; | 				return PLPGSQL_RC_RETURN; | ||||||
| 
 | 
 | ||||||
| @ -1236,7 +1272,7 @@ static int | |||||||
| exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt) | exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt) | ||||||
| { | { | ||||||
| 	bool		value; | 	bool		value; | ||||||
| 	bool		isnull = false; | 	bool		isnull; | ||||||
| 	int			rc; | 	int			rc; | ||||||
| 
 | 
 | ||||||
| 	for (;;) | 	for (;;) | ||||||
| @ -1264,6 +1300,19 @@ exec_stmt_while(PLpgSQL_execstate *estate, PLpgSQL_stmt_while *stmt) | |||||||
| 				estate->exitlabel = NULL; | 				estate->exitlabel = NULL; | ||||||
| 				return PLPGSQL_RC_OK; | 				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: | 			case PLPGSQL_RC_RETURN: | ||||||
| 				return PLPGSQL_RC_RETURN; | 				return PLPGSQL_RC_RETURN; | ||||||
| 
 | 
 | ||||||
| @ -1288,7 +1337,7 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt) | |||||||
| 	PLpgSQL_var *var; | 	PLpgSQL_var *var; | ||||||
| 	Datum		value; | 	Datum		value; | ||||||
| 	Oid			valtype; | 	Oid			valtype; | ||||||
| 	bool		isnull = false; | 	bool		isnull; | ||||||
| 	bool		found = false; | 	bool		found = false; | ||||||
| 	int			rc = PLPGSQL_RC_OK; | 	int			rc = PLPGSQL_RC_OK; | ||||||
| 
 | 
 | ||||||
| @ -1366,13 +1415,35 @@ exec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt) | |||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			/*
 | 			/*
 | ||||||
| 			 * otherwise, we processed a labelled exit that does not match | 			 * otherwise, this is a labelled exit that does not match | ||||||
| 			 * the current statement's label, if any: return RC_EXIT so | 			 * the current statement's label, if any: return RC_EXIT | ||||||
| 			 * that the EXIT continues to recurse upward. | 			 * so that the EXIT continues to propagate up the stack. | ||||||
| 			 */ | 			 */ | ||||||
| 
 | 
 | ||||||
| 			break; | 			break; | ||||||
| 		} | 		} | ||||||
|  | 		else if (rc == PLPGSQL_RC_CONTINUE) | ||||||
|  | 		{ | ||||||
|  | 			if (estate->exitlabel == NULL) | ||||||
|  | 				/* anonymous continue, so continue the current loop */ | ||||||
|  | 				; | ||||||
|  | 			else if (stmt->label != NULL && | ||||||
|  | 					 strcmp(stmt->label, estate->exitlabel) == 0) | ||||||
|  | 			{ | ||||||
|  | 				/* labelled continue, matches the current stmt's label */ | ||||||
|  | 				estate->exitlabel = NULL; | ||||||
|  | 			} | ||||||
|  | 			else | ||||||
|  | 			{ | ||||||
|  | 			    /*
 | ||||||
|  | 				 * otherwise, this is 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. | ||||||
|  | 				 */ | ||||||
|  | 			    break; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 		/*
 | 		/*
 | ||||||
| 		 * Increase/decrease loop var | 		 * Increase/decrease loop var | ||||||
| @ -1459,17 +1530,8 @@ exec_stmt_fors(PLpgSQL_execstate *estate, PLpgSQL_stmt_fors *stmt) | |||||||
| 			 * Execute the statements | 			 * Execute the statements | ||||||
| 			 */ | 			 */ | ||||||
| 			rc = exec_stmts(estate, stmt->body); | 			rc = exec_stmts(estate, stmt->body); | ||||||
| 
 |  | ||||||
| 			if (rc != PLPGSQL_RC_OK) | 			if (rc != PLPGSQL_RC_OK) | ||||||
| 			{ | 			{ | ||||||
| 				/*
 |  | ||||||
| 				 * We're aborting the loop, so cleanup and set FOUND. |  | ||||||
| 				 * (This code should match the code after the loop.) |  | ||||||
| 				 */ |  | ||||||
| 				SPI_freetuptable(tuptab); |  | ||||||
| 				SPI_cursor_close(portal); |  | ||||||
| 				exec_set_found(estate, found); |  | ||||||
| 
 |  | ||||||
| 				if (rc == PLPGSQL_RC_EXIT) | 				if (rc == PLPGSQL_RC_EXIT) | ||||||
| 				{ | 				{ | ||||||
| 					if (estate->exitlabel == NULL) | 					if (estate->exitlabel == NULL) | ||||||
| @ -1490,6 +1552,34 @@ exec_stmt_fors(PLpgSQL_execstate *estate, PLpgSQL_stmt_fors *stmt) | |||||||
| 					 * recurse upward. | 					 * recurse upward. | ||||||
| 					 */ | 					 */ | ||||||
| 				} | 				} | ||||||
|  | 				else if (rc == PLPGSQL_RC_CONTINUE) | ||||||
|  | 				{ | ||||||
|  | 					if (estate->exitlabel == NULL) | ||||||
|  | 						/* unlabelled continue, continue the current loop */ | ||||||
|  | 						continue; | ||||||
|  | 					else if (stmt->label != NULL && | ||||||
|  | 							 strcmp(stmt->label, estate->exitlabel) == 0) | ||||||
|  | 					{ | ||||||
|  | 						/* labelled continue, matches the current stmt's label */ | ||||||
|  | 						estate->exitlabel = NULL; | ||||||
|  | 						continue; | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					/*
 | ||||||
|  | 					 * otherwise, we processed 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, so cleanup and set FOUND. | ||||||
|  | 				 * (This code should match the code after the loop.) | ||||||
|  | 				 */ | ||||||
|  | 				SPI_freetuptable(tuptab); | ||||||
|  | 				SPI_cursor_close(portal); | ||||||
|  | 				exec_set_found(estate, found); | ||||||
| 
 | 
 | ||||||
| 				return rc; | 				return rc; | ||||||
| 			} | 			} | ||||||
| @ -1563,7 +1653,7 @@ exec_stmt_select(PLpgSQL_execstate *estate, PLpgSQL_stmt_select *stmt) | |||||||
| 	n = estate->eval_processed; | 	n = estate->eval_processed; | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * If the query didn't return any row, set the target to NULL and | 	 * If the query didn't return any rows, set the target to NULL and | ||||||
| 	 * return. | 	 * return. | ||||||
| 	 */ | 	 */ | ||||||
| 	if (n == 0) | 	if (n == 0) | ||||||
| @ -1586,28 +1676,33 @@ exec_stmt_select(PLpgSQL_execstate *estate, PLpgSQL_stmt_select *stmt) | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| /* ----------
 | /* ----------
 | ||||||
|  * exec_stmt_exit			Start exiting loop(s) or blocks |  * exec_stmt_exit			Implements EXIT and CONTINUE | ||||||
|  |  * | ||||||
|  |  * This begins the process of exiting / restarting a loop. | ||||||
|  * ---------- |  * ---------- | ||||||
|  */ |  */ | ||||||
| static int | static int | ||||||
| exec_stmt_exit(PLpgSQL_execstate *estate, PLpgSQL_stmt_exit *stmt) | exec_stmt_exit(PLpgSQL_execstate *estate, PLpgSQL_stmt_exit *stmt) | ||||||
| { | { | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * If the exit has a condition, check that it's true | 	 * If the exit / continue has a condition, evaluate it | ||||||
| 	 */ | 	 */ | ||||||
| 	if (stmt->cond != NULL) | 	if (stmt->cond != NULL) | ||||||
| 	{ | 	{ | ||||||
| 		bool		value; | 		bool		value; | ||||||
| 		bool		isnull = false; | 		bool		isnull; | ||||||
| 
 | 
 | ||||||
| 		value = exec_eval_boolean(estate, stmt->cond, &isnull); | 		value = exec_eval_boolean(estate, stmt->cond, &isnull); | ||||||
| 		exec_eval_cleanup(estate); | 		exec_eval_cleanup(estate); | ||||||
| 		if (isnull || !value) | 		if (isnull || value == false) | ||||||
| 			return PLPGSQL_RC_OK; | 			return PLPGSQL_RC_OK; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	estate->exitlabel = stmt->label; | 	estate->exitlabel = stmt->label; | ||||||
|  | 	if (stmt->is_exit) | ||||||
| 		return PLPGSQL_RC_EXIT; | 		return PLPGSQL_RC_EXIT; | ||||||
|  | 	else | ||||||
|  | 		return PLPGSQL_RC_CONTINUE; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -2455,14 +2550,6 @@ exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt) | |||||||
| 
 | 
 | ||||||
| 			if (rc != PLPGSQL_RC_OK) | 			if (rc != PLPGSQL_RC_OK) | ||||||
| 			{ | 			{ | ||||||
| 				/*
 |  | ||||||
| 				 * We're aborting the loop, so cleanup and set FOUND. |  | ||||||
| 				 * (This code should match the code after the loop.) |  | ||||||
| 				 */ |  | ||||||
| 				SPI_freetuptable(tuptab); |  | ||||||
| 				SPI_cursor_close(portal); |  | ||||||
| 				exec_set_found(estate, found); |  | ||||||
| 
 |  | ||||||
| 				if (rc == PLPGSQL_RC_EXIT) | 				if (rc == PLPGSQL_RC_EXIT) | ||||||
| 				{ | 				{ | ||||||
| 					if (estate->exitlabel == NULL) | 					if (estate->exitlabel == NULL) | ||||||
| @ -2483,6 +2570,33 @@ exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt) | |||||||
| 					 * recurse upward. | 					 * recurse upward. | ||||||
| 					 */ | 					 */ | ||||||
| 				} | 				} | ||||||
|  | 				else if (rc == PLPGSQL_RC_CONTINUE) | ||||||
|  | 				{ | ||||||
|  | 					if (estate->exitlabel == NULL) | ||||||
|  | 						/* unlabelled continue, continue the current loop */ | ||||||
|  | 						continue; | ||||||
|  | 					else if (stmt->label != NULL && | ||||||
|  | 							 strcmp(stmt->label, estate->exitlabel) == 0) | ||||||
|  | 					{ | ||||||
|  | 						/* labelled continue, matches the current stmt's label */ | ||||||
|  | 						estate->exitlabel = NULL; | ||||||
|  | 						continue; | ||||||
|  | 					} | ||||||
|  | 
 | ||||||
|  | 					/*
 | ||||||
|  | 					 * otherwise, we process a labelled continue that | ||||||
|  | 					 * does not match the current statement's label, | ||||||
|  | 					 * so propagate RC_CONTINUE upward in the stack. | ||||||
|  | 					 */ | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				/*
 | ||||||
|  | 				 * We're aborting the loop, so cleanup and set FOUND. | ||||||
|  | 				 * (This code should match the code after the loop.) | ||||||
|  | 				 */ | ||||||
|  | 				SPI_freetuptable(tuptab); | ||||||
|  | 				SPI_cursor_close(portal); | ||||||
|  | 				exec_set_found(estate, found); | ||||||
| 
 | 
 | ||||||
| 				return rc; | 				return rc; | ||||||
| 			} | 			} | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ | |||||||
|  *			  procedural language |  *			  procedural language | ||||||
|  * |  * | ||||||
|  * IDENTIFICATION |  * IDENTIFICATION | ||||||
|  *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.44 2005/06/19 01:06:12 momjian Exp $ |  *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.45 2005/06/22 01:35:02 neilc Exp $ | ||||||
|  * |  * | ||||||
|  *	  This software is copyrighted by Jan Wieck - Hamburg. |  *	  This software is copyrighted by Jan Wieck - Hamburg. | ||||||
|  * |  * | ||||||
| @ -845,7 +845,8 @@ static void | |||||||
| dump_exit(PLpgSQL_stmt_exit *stmt) | dump_exit(PLpgSQL_stmt_exit *stmt) | ||||||
| { | { | ||||||
| 	dump_ind(); | 	dump_ind(); | ||||||
| 	printf("EXIT lbl='%s'", stmt->label); | 	printf("%s label='%s'", | ||||||
|  | 		   stmt->is_exit ? "EXIT" : "CONTINUE", stmt->label); | ||||||
| 	if (stmt->cond != NULL) | 	if (stmt->cond != NULL) | ||||||
| 	{ | 	{ | ||||||
| 		printf(" WHEN "); | 		printf(" WHEN "); | ||||||
|  | |||||||
| @ -3,7 +3,7 @@ | |||||||
|  *			  procedural language |  *			  procedural language | ||||||
|  * |  * | ||||||
|  * IDENTIFICATION |  * IDENTIFICATION | ||||||
|  *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.63 2005/06/14 06:43:14 neilc Exp $ |  *	  $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.64 2005/06/22 01:35:02 neilc Exp $ | ||||||
|  * |  * | ||||||
|  *	  This software is copyrighted by Jan Wieck - Hamburg. |  *	  This software is copyrighted by Jan Wieck - Hamburg. | ||||||
|  * |  * | ||||||
| @ -125,7 +125,8 @@ enum | |||||||
| { | { | ||||||
| 	PLPGSQL_RC_OK, | 	PLPGSQL_RC_OK, | ||||||
| 	PLPGSQL_RC_EXIT, | 	PLPGSQL_RC_EXIT, | ||||||
| 	PLPGSQL_RC_RETURN | 	PLPGSQL_RC_RETURN, | ||||||
|  | 	PLPGSQL_RC_CONTINUE | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| /* ----------
 | /* ----------
 | ||||||
| @ -485,9 +486,10 @@ typedef struct | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| typedef struct | typedef struct | ||||||
| {								/* EXIT statement			*/ | {								/* EXIT or CONTINUE statement			*/ | ||||||
| 	int			cmd_type; | 	int			cmd_type; | ||||||
| 	int			lineno; | 	int			lineno; | ||||||
|  | 	bool		is_exit;		/* Is this an exit or a continue? */ | ||||||
| 	char	   *label; | 	char	   *label; | ||||||
| 	PLpgSQL_expr *cond; | 	PLpgSQL_expr *cond; | ||||||
| } PLpgSQL_stmt_exit; | } PLpgSQL_stmt_exit; | ||||||
| @ -610,7 +612,8 @@ typedef struct | |||||||
| 	bool		readonly_func; | 	bool		readonly_func; | ||||||
| 
 | 
 | ||||||
| 	TupleDesc	rettupdesc; | 	TupleDesc	rettupdesc; | ||||||
| 	char	   *exitlabel; | 	char	   *exitlabel;		/* the "target" label of the current
 | ||||||
|  | 								 * EXIT or CONTINUE stmt, if any */ | ||||||
| 
 | 
 | ||||||
| 	Tuplestorestate *tuple_store;		/* SRFs accumulate results here */ | 	Tuplestorestate *tuple_store;		/* SRFs accumulate results here */ | ||||||
| 	MemoryContext tuple_store_cxt; | 	MemoryContext tuple_store_cxt; | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ | |||||||
|  *			  procedural language |  *			  procedural language | ||||||
|  * |  * | ||||||
|  * IDENTIFICATION |  * IDENTIFICATION | ||||||
|  *    $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.40 2005/03/11 19:13:43 momjian Exp $ |  *    $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.41 2005/06/22 01:35:02 neilc Exp $ | ||||||
|  * |  * | ||||||
|  *    This software is copyrighted by Jan Wieck - Hamburg. |  *    This software is copyrighted by Jan Wieck - Hamburg. | ||||||
|  * |  * | ||||||
| @ -139,6 +139,7 @@ alias			{ return K_ALIAS;			} | |||||||
| begin			{ return K_BEGIN;			} | begin			{ return K_BEGIN;			} | ||||||
| close			{ return K_CLOSE;			} | close			{ return K_CLOSE;			} | ||||||
| constant		{ return K_CONSTANT;		} | constant		{ return K_CONSTANT;		} | ||||||
|  | continue		{ return K_CONTINUE;		} | ||||||
| cursor			{ return K_CURSOR;			} | cursor			{ return K_CURSOR;			} | ||||||
| debug			{ return K_DEBUG;			} | debug			{ return K_DEBUG;			} | ||||||
| declare			{ return K_DECLARE;			} | declare			{ return K_DECLARE;			} | ||||||
|  | |||||||
| @ -2491,3 +2491,140 @@ NOTICE:  {10,20,30}; 20; xyz; xyzabc; (10,aaa,,30); <NULL> | |||||||
| (1 row) | (1 row) | ||||||
| 
 | 
 | ||||||
| drop function raise_exprs(); | drop function raise_exprs(); | ||||||
|  | -- continue statement  | ||||||
|  | create table conttesttbl(idx serial, v integer); | ||||||
|  | NOTICE:  CREATE TABLE will create implicit sequence "conttesttbl_idx_seq" for serial column "conttesttbl.idx" | ||||||
|  | insert into conttesttbl(v) values(10); | ||||||
|  | insert into conttesttbl(v) values(20); | ||||||
|  | insert into conttesttbl(v) values(30); | ||||||
|  | insert into conttesttbl(v) values(40); | ||||||
|  | create function continue_test1() returns void as $$ | ||||||
|  | declare _i integer = 0; _r record; | ||||||
|  | begin | ||||||
|  |   raise notice '---1---'; | ||||||
|  |   loop | ||||||
|  |     _i := _i + 1; | ||||||
|  |     raise notice '%', _i; | ||||||
|  |     continue when _i < 10; | ||||||
|  |     exit; | ||||||
|  |   end loop; | ||||||
|  | 
 | ||||||
|  |   raise notice '---2---'; | ||||||
|  |   <<lbl>> | ||||||
|  |   loop | ||||||
|  |     _i := _i - 1; | ||||||
|  |     loop | ||||||
|  |       raise notice '%', _i; | ||||||
|  |       continue lbl when _i > 0; | ||||||
|  |       exit lbl; | ||||||
|  |     end loop; | ||||||
|  |   end loop; | ||||||
|  | 
 | ||||||
|  |   raise notice '---3---'; | ||||||
|  |   <<the_loop>> | ||||||
|  |   while _i < 10 loop | ||||||
|  |     _i := _i + 1; | ||||||
|  |     continue the_loop when _i % 2 = 0; | ||||||
|  |     raise notice '%', _i; | ||||||
|  |   end loop; | ||||||
|  | 
 | ||||||
|  |   raise notice '---4---'; | ||||||
|  |   for _i in 1..10 loop | ||||||
|  |     begin | ||||||
|  |       -- applies to outer loop, not the nested begin block | ||||||
|  |       continue when _i < 5;  | ||||||
|  |       raise notice '%', _i; | ||||||
|  |     end; | ||||||
|  |   end loop; | ||||||
|  | 
 | ||||||
|  |   raise notice '---5---'; | ||||||
|  |   for _r in select * from conttesttbl loop | ||||||
|  |     continue when _r.v <= 20; | ||||||
|  |     raise notice '%', _r.v; | ||||||
|  |   end loop; | ||||||
|  | 
 | ||||||
|  |   raise notice '---6---'; | ||||||
|  |   for _r in execute 'select * from conttesttbl' loop | ||||||
|  |     continue when _r.v <= 20; | ||||||
|  |     raise notice '%', _r.v; | ||||||
|  |   end loop;   | ||||||
|  | end; $$ language plpgsql; | ||||||
|  | select continue_test1(); | ||||||
|  | NOTICE:  ---1--- | ||||||
|  | NOTICE:  1 | ||||||
|  | NOTICE:  2 | ||||||
|  | NOTICE:  3 | ||||||
|  | NOTICE:  4 | ||||||
|  | NOTICE:  5 | ||||||
|  | NOTICE:  6 | ||||||
|  | NOTICE:  7 | ||||||
|  | NOTICE:  8 | ||||||
|  | NOTICE:  9 | ||||||
|  | NOTICE:  10 | ||||||
|  | NOTICE:  ---2--- | ||||||
|  | NOTICE:  9 | ||||||
|  | NOTICE:  8 | ||||||
|  | NOTICE:  7 | ||||||
|  | NOTICE:  6 | ||||||
|  | NOTICE:  5 | ||||||
|  | NOTICE:  4 | ||||||
|  | NOTICE:  3 | ||||||
|  | NOTICE:  2 | ||||||
|  | NOTICE:  1 | ||||||
|  | NOTICE:  0 | ||||||
|  | NOTICE:  ---3--- | ||||||
|  | NOTICE:  1 | ||||||
|  | NOTICE:  3 | ||||||
|  | NOTICE:  5 | ||||||
|  | NOTICE:  7 | ||||||
|  | NOTICE:  9 | ||||||
|  | NOTICE:  ---4--- | ||||||
|  | NOTICE:  5 | ||||||
|  | NOTICE:  6 | ||||||
|  | NOTICE:  7 | ||||||
|  | NOTICE:  8 | ||||||
|  | NOTICE:  9 | ||||||
|  | NOTICE:  10 | ||||||
|  | NOTICE:  ---5--- | ||||||
|  | NOTICE:  30 | ||||||
|  | NOTICE:  40 | ||||||
|  | NOTICE:  ---6--- | ||||||
|  | NOTICE:  30 | ||||||
|  | NOTICE:  40 | ||||||
|  |  continue_test1  | ||||||
|  | ---------------- | ||||||
|  |   | ||||||
|  | (1 row) | ||||||
|  | 
 | ||||||
|  | -- CONTINUE is only legal inside a loop | ||||||
|  | create function continue_test2() returns void as $$ | ||||||
|  | begin | ||||||
|  |     begin | ||||||
|  |         continue; | ||||||
|  |     end; | ||||||
|  |     return; | ||||||
|  | end; | ||||||
|  | $$ language plpgsql; | ||||||
|  | -- should fail | ||||||
|  | select continue_test2(); | ||||||
|  | ERROR:  CONTINUE cannot be used outside a loop | ||||||
|  | CONTEXT:  PL/pgSQL function "continue_test2" | ||||||
|  | -- CONTINUE can't reference the label of a named block | ||||||
|  | create function continue_test3() returns void as $$ | ||||||
|  | begin | ||||||
|  |     <<begin_block1>> | ||||||
|  |     begin | ||||||
|  |         loop | ||||||
|  |             continue begin_block1; | ||||||
|  |         end loop; | ||||||
|  |     end; | ||||||
|  | end; | ||||||
|  | $$ language plpgsql; | ||||||
|  | -- should fail | ||||||
|  | select continue_test3(); | ||||||
|  | ERROR:  CONTINUE cannot be used outside a loop | ||||||
|  | CONTEXT:  PL/pgSQL function "continue_test3" | ||||||
|  | drop function continue_test1(); | ||||||
|  | drop function continue_test2(); | ||||||
|  | drop function continue_test3(); | ||||||
|  | drop table conttesttbl; | ||||||
|  | |||||||
| @ -2112,3 +2112,97 @@ end;$$ language plpgsql; | |||||||
| 
 | 
 | ||||||
| select raise_exprs(); | select raise_exprs(); | ||||||
| drop function raise_exprs(); | drop function raise_exprs(); | ||||||
|  | 
 | ||||||
|  | -- continue statement  | ||||||
|  | create table conttesttbl(idx serial, v integer); | ||||||
|  | insert into conttesttbl(v) values(10); | ||||||
|  | insert into conttesttbl(v) values(20); | ||||||
|  | insert into conttesttbl(v) values(30); | ||||||
|  | insert into conttesttbl(v) values(40); | ||||||
|  | 
 | ||||||
|  | create function continue_test1() returns void as $$ | ||||||
|  | declare _i integer = 0; _r record; | ||||||
|  | begin | ||||||
|  |   raise notice '---1---'; | ||||||
|  |   loop | ||||||
|  |     _i := _i + 1; | ||||||
|  |     raise notice '%', _i; | ||||||
|  |     continue when _i < 10; | ||||||
|  |     exit; | ||||||
|  |   end loop; | ||||||
|  | 
 | ||||||
|  |   raise notice '---2---'; | ||||||
|  |   <<lbl>> | ||||||
|  |   loop | ||||||
|  |     _i := _i - 1; | ||||||
|  |     loop | ||||||
|  |       raise notice '%', _i; | ||||||
|  |       continue lbl when _i > 0; | ||||||
|  |       exit lbl; | ||||||
|  |     end loop; | ||||||
|  |   end loop; | ||||||
|  | 
 | ||||||
|  |   raise notice '---3---'; | ||||||
|  |   <<the_loop>> | ||||||
|  |   while _i < 10 loop | ||||||
|  |     _i := _i + 1; | ||||||
|  |     continue the_loop when _i % 2 = 0; | ||||||
|  |     raise notice '%', _i; | ||||||
|  |   end loop; | ||||||
|  | 
 | ||||||
|  |   raise notice '---4---'; | ||||||
|  |   for _i in 1..10 loop | ||||||
|  |     begin | ||||||
|  |       -- applies to outer loop, not the nested begin block | ||||||
|  |       continue when _i < 5;  | ||||||
|  |       raise notice '%', _i; | ||||||
|  |     end; | ||||||
|  |   end loop; | ||||||
|  | 
 | ||||||
|  |   raise notice '---5---'; | ||||||
|  |   for _r in select * from conttesttbl loop | ||||||
|  |     continue when _r.v <= 20; | ||||||
|  |     raise notice '%', _r.v; | ||||||
|  |   end loop; | ||||||
|  | 
 | ||||||
|  |   raise notice '---6---'; | ||||||
|  |   for _r in execute 'select * from conttesttbl' loop | ||||||
|  |     continue when _r.v <= 20; | ||||||
|  |     raise notice '%', _r.v; | ||||||
|  |   end loop;   | ||||||
|  | end; $$ language plpgsql; | ||||||
|  | 
 | ||||||
|  | select continue_test1(); | ||||||
|  | 
 | ||||||
|  | -- CONTINUE is only legal inside a loop | ||||||
|  | create function continue_test2() returns void as $$ | ||||||
|  | begin | ||||||
|  |     begin | ||||||
|  |         continue; | ||||||
|  |     end; | ||||||
|  |     return; | ||||||
|  | end; | ||||||
|  | $$ language plpgsql; | ||||||
|  | 
 | ||||||
|  | -- should fail | ||||||
|  | select continue_test2(); | ||||||
|  | 
 | ||||||
|  | -- CONTINUE can't reference the label of a named block | ||||||
|  | create function continue_test3() returns void as $$ | ||||||
|  | begin | ||||||
|  |     <<begin_block1>> | ||||||
|  |     begin | ||||||
|  |         loop | ||||||
|  |             continue begin_block1; | ||||||
|  |         end loop; | ||||||
|  |     end; | ||||||
|  | end; | ||||||
|  | $$ language plpgsql; | ||||||
|  | 
 | ||||||
|  | -- should fail | ||||||
|  | select continue_test3(); | ||||||
|  | 
 | ||||||
|  | drop function continue_test1(); | ||||||
|  | drop function continue_test2(); | ||||||
|  | drop function continue_test3(); | ||||||
|  | drop table conttesttbl; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user