]> granicus.if.org Git - postgresql/blobdiff - src/pl/plpgsql/src/pl_exec.c
pgindent run for 9.0, second run
[postgresql] / src / pl / plpgsql / src / pl_exec.c
index 117da74eb0f109df2e899b49f6680bdb55ef754d..e1f48e3d75d55cdef7f700b40c0839787923d41c 100644 (file)
@@ -3,12 +3,12 @@
  * pl_exec.c           - Executor for the PL/pgSQL
  *                       procedural language
  *
- * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.251 2009/11/09 00:26:55 tgl Exp $
+ *       $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.261 2010/07/06 19:19:01 momjian Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -171,7 +171,7 @@ static int exec_run_select(PLpgSQL_execstate *estate,
 static int exec_for_query(PLpgSQL_execstate *estate, PLpgSQL_stmt_forq *stmt,
                           Portal portal, bool prefetch_ok);
 static ParamListInfo setup_param_list(PLpgSQL_execstate *estate,
-                                                                         PLpgSQL_expr *expr);
+                                PLpgSQL_expr *expr);
 static void plpgsql_param_fetch(ParamListInfo params, int paramid);
 static void exec_move_row(PLpgSQL_execstate *estate,
                          PLpgSQL_rec *rec,
@@ -200,7 +200,8 @@ static PreparedParamsData *exec_eval_using_params(PLpgSQL_execstate *estate,
                                           List *params);
 static void free_params_data(PreparedParamsData *ppd);
 static Portal exec_dynquery_with_params(PLpgSQL_execstate *estate,
-                                                 PLpgSQL_expr *query, List *params);
+                                                 PLpgSQL_expr *dynquery, List *params,
+                                                 const char *portalname, int cursorOptions);
 
 
 /* ----------
@@ -514,10 +515,10 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
        /*
         * Put the OLD and NEW tuples into record variables
         *
-        * We make the tupdescs available in both records even though only one
-        * may have a value.  This allows parsing of record references to succeed
-        * in functions that are used for multiple trigger types.  For example,
-        * we might have a test like "if (TG_OP = 'INSERT' and NEW.foo = 'xyz')",
+        * We make the tupdescs available in both records even though only one may
+        * have a value.  This allows parsing of record references to succeed in
+        * functions that are used for multiple trigger types.  For example, we
+        * might have a test like "if (TG_OP = 'INSERT' and NEW.foo = 'xyz')",
         * which should parse regardless of the current trigger type.
         */
        rec_new = (PLpgSQL_rec *) (estate.datums[func->new_varno]);
@@ -1898,6 +1899,7 @@ exec_stmt_forc(PLpgSQL_execstate *estate, PLpgSQL_stmt_forc *stmt)
 {
        PLpgSQL_var *curvar;
        char       *curname = NULL;
+       const char *portalname;
        PLpgSQL_expr *query;
        ParamListInfo paramLI;
        Portal          portal;
@@ -1967,8 +1969,8 @@ exec_stmt_forc(PLpgSQL_execstate *estate, PLpgSQL_stmt_forc *stmt)
                exec_prepare_plan(estate, query, curvar->cursor_options);
 
        /*
-        * Set up ParamListInfo (note this is only carrying a hook function,
-        * not any actual data values, at this point)
+        * Set up ParamListInfo (note this is only carrying a hook function, not
+        * any actual data values, at this point)
         */
        paramLI = setup_param_list(estate, query);
 
@@ -1981,6 +1983,7 @@ exec_stmt_forc(PLpgSQL_execstate *estate, PLpgSQL_stmt_forc *stmt)
        if (portal == NULL)
                elog(ERROR, "could not open cursor: %s",
                         SPI_result_code_string(SPI_result));
+       portalname = portal->name;
 
        /* don't need paramlist any more */
        if (paramLI)
@@ -2172,7 +2175,6 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
 {
        TupleDesc       tupdesc;
        int                     natts;
-       MemoryContext oldcxt;
        HeapTuple       tuple = NULL;
        bool            free_tuple = false;
 
@@ -2212,10 +2214,8 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
                                                                                                tupdesc->attrs[0]->atttypmod,
                                                                                                        isNull);
 
-                                       oldcxt = MemoryContextSwitchTo(estate->tuple_store_cxt);
                                        tuplestore_putvalues(estate->tuple_store, tupdesc,
                                                                                 &retval, &isNull);
-                                       MemoryContextSwitchTo(oldcxt);
                                }
                                break;
 
@@ -2285,10 +2285,8 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
                                                                                tupdesc->attrs[0]->atttypmod,
                                                                                isNull);
 
-               oldcxt = MemoryContextSwitchTo(estate->tuple_store_cxt);
                tuplestore_putvalues(estate->tuple_store, tupdesc,
                                                         &retval, &isNull);
-               MemoryContextSwitchTo(oldcxt);
 
                exec_eval_cleanup(estate);
        }
@@ -2301,9 +2299,7 @@ exec_stmt_return_next(PLpgSQL_execstate *estate,
 
        if (HeapTupleIsValid(tuple))
        {
-               oldcxt = MemoryContextSwitchTo(estate->tuple_store_cxt);
                tuplestore_puttuple(estate->tuple_store, tuple);
-               MemoryContextSwitchTo(oldcxt);
 
                if (free_tuple)
                        heap_freetuple(tuple);
@@ -2344,23 +2340,21 @@ exec_stmt_return_query(PLpgSQL_execstate *estate,
                /* RETURN QUERY EXECUTE */
                Assert(stmt->dynquery != NULL);
                portal = exec_dynquery_with_params(estate, stmt->dynquery,
-                                                                                  stmt->params);
+                                                                                  stmt->params, NULL, 0);
        }
 
        tupmap = convert_tuples_by_position(portal->tupDesc,
                                                                                estate->rettupdesc,
-                                                                               gettext_noop("structure of query does not match function result type"));
+        gettext_noop("structure of query does not match function result type"));
 
        while (true)
        {
-               MemoryContext old_cxt;
                int                     i;
 
                SPI_cursor_fetch(portal, true, 50);
                if (SPI_processed == 0)
                        break;
 
-               old_cxt = MemoryContextSwitchTo(estate->tuple_store_cxt);
                for (i = 0; i < SPI_processed; i++)
                {
                        HeapTuple       tuple = SPI_tuptable->vals[i];
@@ -2372,7 +2366,6 @@ exec_stmt_return_query(PLpgSQL_execstate *estate,
                                heap_freetuple(tuple);
                        processed++;
                }
-               MemoryContextSwitchTo(old_cxt);
 
                SPI_freetuptable(SPI_tuptable);
        }
@@ -2394,6 +2387,7 @@ exec_init_tuple_store(PLpgSQL_execstate *estate)
 {
        ReturnSetInfo *rsi = estate->rsi;
        MemoryContext oldcxt;
+       ResourceOwner oldowner;
 
        /*
         * Check caller can handle a set result in the way we want
@@ -2405,12 +2399,22 @@ exec_init_tuple_store(PLpgSQL_execstate *estate)
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                 errmsg("set-valued function called in context that cannot accept a set")));
 
-       estate->tuple_store_cxt = rsi->econtext->ecxt_per_query_memory;
-
+       /*
+        * Switch to the right memory context and resource owner for storing the
+        * tuplestore for return set. If we're within a subtransaction opened for
+        * an exception-block, for example, we must still create the tuplestore in
+        * the resource owner that was active when this function was entered, and
+        * not in the subtransaction resource owner.
+        */
        oldcxt = MemoryContextSwitchTo(estate->tuple_store_cxt);
+       oldowner = CurrentResourceOwner;
+       CurrentResourceOwner = estate->tuple_store_owner;
+
        estate->tuple_store =
                tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
                                                          false, work_mem);
+
+       CurrentResourceOwner = oldowner;
        MemoryContextSwitchTo(oldcxt);
 
        estate->rettupdesc = rsi->expectedDesc;
@@ -2443,7 +2447,7 @@ exec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt)
 
        if (stmt->message)
        {
-               StringInfoData  ds;
+               StringInfoData ds;
                ListCell   *current_param;
                char       *cp;
 
@@ -2635,7 +2639,16 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
        estate->exitlabel = NULL;
 
        estate->tuple_store = NULL;
-       estate->tuple_store_cxt = NULL;
+       if (rsi)
+       {
+               estate->tuple_store_cxt = rsi->econtext->ecxt_per_query_memory;
+               estate->tuple_store_owner = CurrentResourceOwner;
+       }
+       else
+       {
+               estate->tuple_store_cxt = NULL;
+               estate->tuple_store_owner = NULL;
+       }
        estate->rsi = rsi;
 
        estate->found_varno = func->found_varno;
@@ -2707,8 +2720,8 @@ exec_prepare_plan(PLpgSQL_execstate *estate,
        SPIPlanPtr      plan;
 
        /*
-        * The grammar can't conveniently set expr->func while building the
-        * parse tree, so make sure it's set before parser hooks need it.
+        * The grammar can't conveniently set expr->func while building the parse
+        * tree, so make sure it's set before parser hooks need it.
         */
        expr->func = estate->func;
 
@@ -2789,8 +2802,8 @@ exec_stmt_execsql(PLpgSQL_execstate *estate,
        }
 
        /*
-        * Set up ParamListInfo (note this is only carrying a hook function,
-        * not any actual data values, at this point)
+        * Set up ParamListInfo (note this is only carrying a hook function, not
+        * any actual data values, at this point)
         */
        paramLI = setup_param_list(estate, expr);
 
@@ -3022,7 +3035,8 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate,
                                if (*ptr == 'S' || *ptr == 's')
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-                                       errmsg("EXECUTE of SELECT ... INTO is not implemented")));
+                                        errmsg("EXECUTE of SELECT ... INTO is not implemented"),
+                                                        errhint("You might want to use EXECUTE ... INTO instead.")));
                                break;
                        }
 
@@ -3123,7 +3137,8 @@ exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt)
        Portal          portal;
        int                     rc;
 
-       portal = exec_dynquery_with_params(estate, stmt->query, stmt->params);
+       portal = exec_dynquery_with_params(estate, stmt->query, stmt->params,
+                                                                          NULL, 0);
 
        /*
         * Execute the loop
@@ -3151,7 +3166,6 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt)
        PLpgSQL_expr *query;
        Portal          portal;
        ParamListInfo paramLI;
-       bool            isnull;
 
        /* ----------
         * Get the cursor variable and if it has an assigned name, check
@@ -3191,43 +3205,11 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt)
                 * This is an OPEN refcursor FOR EXECUTE ...
                 * ----------
                 */
-               Datum           queryD;
-               Oid                     restype;
-               char       *querystr;
-               SPIPlanPtr      curplan;
-
-               /* ----------
-                * We evaluate the string expression after the
-                * EXECUTE keyword. It's result is the querystring we have
-                * to execute.
-                * ----------
-                */
-               queryD = exec_eval_expr(estate, stmt->dynquery, &isnull, &restype);
-               if (isnull)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
-                                        errmsg("query string argument of EXECUTE is null")));
-
-               /* Get the C-String representation */
-               querystr = convert_value_to_string(queryD, restype);
-
-               exec_eval_cleanup(estate);
-
-               /* ----------
-                * Now we prepare a query plan for it and open a cursor
-                * ----------
-                */
-               curplan = SPI_prepare_cursor(querystr, 0, NULL, stmt->cursor_options);
-               if (curplan == NULL)
-                       elog(ERROR, "SPI_prepare_cursor failed for \"%s\": %s",
-                                querystr, SPI_result_code_string(SPI_result));
-               portal = SPI_cursor_open(curname, curplan, NULL, NULL,
-                                                                estate->readonly_func);
-               if (portal == NULL)
-                       elog(ERROR, "could not open cursor for query \"%s\": %s",
-                                querystr, SPI_result_code_string(SPI_result));
-               pfree(querystr);
-               SPI_freeplan(curplan);
+               portal = exec_dynquery_with_params(estate,
+                                                                                  stmt->dynquery,
+                                                                                  stmt->params,
+                                                                                  curname,
+                                                                                  stmt->cursor_options);
 
                /*
                 * If cursor variable was NULL, store the generated portal name in it
@@ -3287,8 +3269,8 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt)
        }
 
        /*
-        * Set up ParamListInfo (note this is only carrying a hook function,
-        * not any actual data values, at this point)
+        * Set up ParamListInfo (note this is only carrying a hook function, not
+        * any actual data values, at this point)
         */
        paramLI = setup_param_list(estate, query);
 
@@ -3541,11 +3523,6 @@ exec_assign_value(PLpgSQL_execstate *estate,
                                 */
                                PLpgSQL_row *row = (PLpgSQL_row *) target;
 
-                               /* Source must be of RECORD or composite type */
-                               if (!type_is_rowtype(valtype))
-                                       ereport(ERROR,
-                                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
-                                                        errmsg("cannot assign non-composite value to a row variable")));
                                if (*isNull)
                                {
                                        /* If source is null, just assign nulls to the row */
@@ -3559,7 +3536,12 @@ exec_assign_value(PLpgSQL_execstate *estate,
                                        TupleDesc       tupdesc;
                                        HeapTupleData tmptup;
 
-                                       /* Else source is a tuple Datum, safe to do this: */
+                                       /* Source must be of RECORD or composite type */
+                                       if (!type_is_rowtype(valtype))
+                                               ereport(ERROR,
+                                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                                                errmsg("cannot assign non-composite value to a row variable")));
+                                       /* Source is a tuple Datum, so safe to do this: */
                                        td = DatumGetHeapTupleHeader(value);
                                        /* Extract rowtype info and find a tupdesc */
                                        tupType = HeapTupleHeaderGetTypeId(td);
@@ -3583,11 +3565,6 @@ exec_assign_value(PLpgSQL_execstate *estate,
                                 */
                                PLpgSQL_rec *rec = (PLpgSQL_rec *) target;
 
-                               /* Source must be of RECORD or composite type */
-                               if (!type_is_rowtype(valtype))
-                                       ereport(ERROR,
-                                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
-                                                        errmsg("cannot assign non-composite value to a record variable")));
                                if (*isNull)
                                {
                                        /* If source is null, just assign nulls to the record */
@@ -3601,7 +3578,13 @@ exec_assign_value(PLpgSQL_execstate *estate,
                                        TupleDesc       tupdesc;
                                        HeapTupleData tmptup;
 
-                                       /* Else source is a tuple Datum, safe to do this: */
+                                       /* Source must be of RECORD or composite type */
+                                       if (!type_is_rowtype(valtype))
+                                               ereport(ERROR,
+                                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                                                errmsg("cannot assign non-composite value to a record variable")));
+
+                                       /* Source is a tuple Datum, so safe to do this: */
                                        td = DatumGetHeapTupleHeader(value);
                                        /* Extract rowtype info and find a tupdesc */
                                        tupType = HeapTupleHeaderGetTypeId(td);
@@ -4055,7 +4038,7 @@ exec_get_datum_type(PLpgSQL_execstate *estate,
 
                default:
                        elog(ERROR, "unrecognized dtype: %d", datum->dtype);
-                       typeid = InvalidOid;                    /* keep compiler quiet */
+                       typeid = InvalidOid;    /* keep compiler quiet */
                        break;
        }
 
@@ -4178,7 +4161,24 @@ exec_eval_expr(PLpgSQL_execstate *estate,
                                 errmsg("query \"%s\" did not return data", expr->query)));
 
        /*
-        * If there are no rows selected, the result is NULL.
+        * Check that the expression returns exactly one column...
+        */
+       if (estate->eval_tuptable->tupdesc->natts != 1)
+               ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                errmsg_plural("query \"%s\" returned %d column",
+                                                          "query \"%s\" returned %d columns",
+                                                          estate->eval_tuptable->tupdesc->natts,
+                                                          expr->query,
+                                                          estate->eval_tuptable->tupdesc->natts)));
+
+       /*
+        * ... and get the column's datatype.
+        */
+       *rettype = SPI_gettypeid(estate->eval_tuptable->tupdesc, 1);
+
+       /*
+        * If there are no rows selected, the result is a NULL of that type.
         */
        if (estate->eval_processed == 0)
        {
@@ -4187,26 +4187,17 @@ exec_eval_expr(PLpgSQL_execstate *estate,
        }
 
        /*
-        * Check that the expression returned one single Datum
+        * Check that the expression returned no more than one row.
         */
-       if (estate->eval_processed > 1)
+       if (estate->eval_processed != 1)
                ereport(ERROR,
                                (errcode(ERRCODE_CARDINALITY_VIOLATION),
                                 errmsg("query \"%s\" returned more than one row",
                                                expr->query)));
-       if (estate->eval_tuptable->tupdesc->natts != 1)
-               ereport(ERROR,
-                               (errcode(ERRCODE_SYNTAX_ERROR),
-                                errmsg_plural("query \"%s\" returned %d column",
-                                                          "query \"%s\" returned %d columns",
-                                                          estate->eval_tuptable->tupdesc->natts,
-                                                          expr->query,
-                                                          estate->eval_tuptable->tupdesc->natts)));
 
        /*
-        * Return the result and its type
+        * Return the single result Datum.
         */
-       *rettype = SPI_gettypeid(estate->eval_tuptable->tupdesc, 1);
        return SPI_getbinval(estate->eval_tuptable->vals[0],
                                                 estate->eval_tuptable->tupdesc, 1, isNull);
 }
@@ -4230,8 +4221,8 @@ exec_run_select(PLpgSQL_execstate *estate,
                exec_prepare_plan(estate, expr, 0);
 
        /*
-        * Set up ParamListInfo (note this is only carrying a hook function,
-        * not any actual data values, at this point)
+        * Set up ParamListInfo (note this is only carrying a hook function, not
+        * any actual data values, at this point)
         */
        paramLI = setup_param_list(estate, expr);
 
@@ -4300,6 +4291,12 @@ exec_for_query(PLpgSQL_execstate *estate, PLpgSQL_stmt_forq *stmt,
        else
                elog(ERROR, "unsupported target");
 
+       /*
+        * Make sure the portal doesn't get closed by the user statements we
+        * execute.
+        */
+       PinPortal(portal);
+
        /*
         * Fetch the initial tuple(s).  If prefetching is allowed then we grab a
         * few more rows to avoid multiple trips through executor startup
@@ -4410,6 +4407,8 @@ loop_exit:
         */
        SPI_freetuptable(tuptab);
 
+       UnpinPortal(portal);
+
        /*
         * Set the FOUND variable to indicate the result of executing the loop
         * (namely, whether we looped one or more times). This must be set last so
@@ -4517,9 +4516,9 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
        }
 
        /*
-        * Create the param list in econtext's temporary memory context.
-        * We won't need to free it explicitly, since it will go away at the
-        * next reset of that context.
+        * Create the param list in econtext's temporary memory context. We won't
+        * need to free it explicitly, since it will go away at the next reset of
+        * that context.
         *
         * XXX think about avoiding repeated palloc's for param lists?  It should
         * be possible --- this routine isn't re-entrant anymore.
@@ -4567,7 +4566,7 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
  *
  * The ParamListInfo array is initially all zeroes, in particular the
  * ptype values are all InvalidOid.  This causes the executor to call the
- * paramFetch hook each time it wants a value.  We thus evaluate only the
+ * paramFetch hook each time it wants a value. We thus evaluate only the
  * parameters actually demanded.
  *
  * The result is a locally palloc'd array that should be pfree'd after use;
@@ -4579,16 +4578,16 @@ setup_param_list(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
        ParamListInfo paramLI;
 
        /*
-        * Could we re-use these arrays instead of palloc'ing a new one each
-        * time?  However, we'd have to zero the array each time anyway,
-        * since new values might have been assigned to the variables.
+        * Could we re-use these arrays instead of palloc'ing a new one each time?
+        * However, we'd have to zero the array each time anyway, since new values
+        * might have been assigned to the variables.
         */
        if (estate->ndatums > 0)
        {
                /* sizeof(ParamListInfoData) includes the first array element */
                paramLI = (ParamListInfo)
                        palloc0(sizeof(ParamListInfoData) +
-                                       (estate->ndatums - 1) * sizeof(ParamExternData));
+                                       (estate->ndatums - 1) *sizeof(ParamExternData));
                paramLI->paramFetch = plpgsql_param_fetch;
                paramLI->paramFetchArg = (void *) estate;
                paramLI->parserSetup = (ParserSetupHook) plpgsql_parser_setup;
@@ -4597,15 +4596,15 @@ setup_param_list(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
 
                /*
                 * Set up link to active expr where the hook functions can find it.
-                * Callers must save and restore cur_expr if there is any chance
-                * that they are interrupting an active use of parameters.
+                * Callers must save and restore cur_expr if there is any chance that
+                * they are interrupting an active use of parameters.
                 */
                estate->cur_expr = expr;
 
                /*
-                * Also make sure this is set before parser hooks need it.  There
-                * is no need to save and restore, since the value is always correct
-                * once set.
+                * Also make sure this is set before parser hooks need it.      There is
+                * no need to save and restore, since the value is always correct once
+                * set.
                 */
                expr->func = estate->func;
        }
@@ -4636,9 +4635,9 @@ plpgsql_param_fetch(ParamListInfo params, int paramid)
        Assert(params->numParams == estate->ndatums);
 
        /*
-        * Do nothing if asked for a value that's not supposed to be used by
-        * this SQL expression.  This avoids unwanted evaluations when functions
-        * such as copyParamList try to materialize all the values.
+        * Do nothing if asked for a value that's not supposed to be used by this
+        * SQL expression.      This avoids unwanted evaluations when functions such
+        * as copyParamList try to materialize all the values.
         */
        if (!bms_is_member(dno, expr->paramnos))
                return;
@@ -4780,6 +4779,11 @@ exec_move_row(PLpgSQL_execstate *estate,
                        {
                                value = (Datum) 0;
                                isnull = true;
+
+                               /*
+                                * InvalidOid is OK because exec_assign_value doesn't care
+                                * about the type of a source NULL
+                                */
                                valtype = InvalidOid;
                        }
 
@@ -5520,8 +5524,11 @@ free_params_data(PreparedParamsData *ppd)
  * Open portal for dynamic query
  */
 static Portal
-exec_dynquery_with_params(PLpgSQL_execstate *estate, PLpgSQL_expr *dynquery,
-                                                 List *params)
+exec_dynquery_with_params(PLpgSQL_execstate *estate,
+                                                 PLpgSQL_expr *dynquery,
+                                                 List *params,
+                                                 const char *portalname,
+                                                 int cursorOptions)
 {
        Portal          portal;
        Datum           query;
@@ -5554,20 +5561,22 @@ exec_dynquery_with_params(PLpgSQL_execstate *estate, PLpgSQL_expr *dynquery,
                PreparedParamsData *ppd;
 
                ppd = exec_eval_using_params(estate, params);
-               portal = SPI_cursor_open_with_args(NULL,
+               portal = SPI_cursor_open_with_args(portalname,
                                                                                   querystr,
                                                                                   ppd->nargs, ppd->types,
                                                                                   ppd->values, ppd->nulls,
-                                                                                  estate->readonly_func, 0);
+                                                                                  estate->readonly_func,
+                                                                                  cursorOptions);
                free_params_data(ppd);
        }
        else
        {
-               portal = SPI_cursor_open_with_args(NULL,
+               portal = SPI_cursor_open_with_args(portalname,
                                                                                   querystr,
                                                                                   0, NULL,
                                                                                   NULL, NULL,
-                                                                                  estate->readonly_func, 0);
+                                                                                  estate->readonly_func,
+                                                                                  cursorOptions);
        }
 
        if (portal == NULL)