88 *
99 *
1010 * IDENTIFICATION
11- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.258 2010/05/31 20:02:30 momjian Exp $
11+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.259 2010/06/21 09:47:29 heikki Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
@@ -1899,6 +1899,7 @@ exec_stmt_forc(PLpgSQL_execstate *estate, PLpgSQL_stmt_forc *stmt)
18991899{
19001900 PLpgSQL_var * curvar ;
19011901 char * curname = NULL ;
1902+ const char * portalname ;
19021903 PLpgSQL_expr * query ;
19031904 ParamListInfo paramLI ;
19041905 Portal portal ;
@@ -1982,6 +1983,7 @@ exec_stmt_forc(PLpgSQL_execstate *estate, PLpgSQL_stmt_forc *stmt)
19821983 if (portal == NULL )
19831984 elog (ERROR , "could not open cursor: %s" ,
19841985 SPI_result_code_string (SPI_result ));
1986+ portalname = portal -> name ;
19851987
19861988 /* don't need paramlist any more */
19871989 if (paramLI )
@@ -2000,11 +2002,20 @@ exec_stmt_forc(PLpgSQL_execstate *estate, PLpgSQL_stmt_forc *stmt)
20002002 rc = exec_for_query (estate , (PLpgSQL_stmt_forq * ) stmt , portal , false);
20012003
20022004 /* ----------
2003- * Close portal, and restore cursor variable if it was initially NULL.
2005+ * Close portal. The statements executed in the loop might've closed the
2006+ * cursor already, rendering our portal pointer invalid, so we mustn't
2007+ * trust the pointer.
20042008 * ----------
20052009 */
2010+ portal = SPI_cursor_find (portalname );
2011+ if (portal == NULL )
2012+ ereport (ERROR ,
2013+ (errcode (ERRCODE_UNDEFINED_CURSOR ),
2014+ errmsg ("cursor \"%s\" closed unexpectedly" ,
2015+ portalname )));
20062016 SPI_cursor_close (portal );
20072017
2018+ /* Restore cursor variable if it was initially NULL. */
20082019 if (curname == NULL )
20092020 {
20102021 free_var (curvar );
@@ -4267,6 +4278,13 @@ exec_run_select(PLpgSQL_execstate *estate,
42674278 * exec_for_query --- execute body of FOR loop for each row from a portal
42684279 *
42694280 * Used by exec_stmt_fors, exec_stmt_forc and exec_stmt_dynfors
4281+ *
4282+ * If the portal is for a cursor that's visible to user code, the statements
4283+ * we execute might move or close the cursor. You must pass prefetch_ok=false
4284+ * in that case to disable optimizations that rely on the portal staying
4285+ * unchanged over execution of the user statements.
4286+ * NB: With prefetch_ok=false, the portal pointer might point to garbage
4287+ * after the call. Caller beware!
42704288 */
42714289static int
42724290exec_for_query (PLpgSQL_execstate * estate , PLpgSQL_stmt_forq * stmt ,
@@ -4278,6 +4296,10 @@ exec_for_query(PLpgSQL_execstate *estate, PLpgSQL_stmt_forq *stmt,
42784296 bool found = false;
42794297 int rc = PLPGSQL_RC_OK ;
42804298 int n ;
4299+ const char * portalname ;
4300+
4301+ /* Remember portal name so that we can re-find it */
4302+ portalname = portal -> name ;
42814303
42824304 /*
42834305 * Determine if we assign to a record or a row
@@ -4386,8 +4408,22 @@ exec_for_query(PLpgSQL_execstate *estate, PLpgSQL_stmt_forq *stmt,
43864408
43874409 /*
43884410 * Fetch more tuples. If prefetching is allowed, grab 50 at a time.
4411+ * Otherwise the statements executed in the loop might've moved or
4412+ * even closed the cursor, so check that the cursor is still open,
4413+ * and fetch only one row at a time.
43894414 */
4390- SPI_cursor_fetch (portal , true, prefetch_ok ? 50 : 1 );
4415+ if (prefetch_ok )
4416+ SPI_cursor_fetch (portal , true, 50 );
4417+ else
4418+ {
4419+ portal = SPI_cursor_find (portalname );
4420+ if (portal == NULL )
4421+ ereport (ERROR ,
4422+ (errcode (ERRCODE_UNDEFINED_CURSOR ),
4423+ errmsg ("cursor \"%s\" closed unexpectedly" ,
4424+ portalname )));
4425+ SPI_cursor_fetch (portal , true, 1 );
4426+ }
43914427 tuptab = SPI_tuptable ;
43924428 n = SPI_processed ;
43934429 }
0 commit comments