Rethinking the parameter access hooks for plpgsql's benefit

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Rethinking the parameter access hooks for plpgsql's benefit
Дата
Msg-id 4146.1425872254@sss.pgh.pa.us
обсуждение исходный текст
Ответы Re: Rethinking the parameter access hooks for plpgsql's benefit  (Robert Haas <robertmhaas@gmail.com>)
Список pgsql-hackers
We already knew that plpgsql's setup_param_list() is a bit of a
performance hog.  Trying to improve that was the primary motivation
behind commit f4e031c662a6b600b786c4849968a099c58fcce7 (the
bms_next_member business), though that was just micro-optimization
rather than any fundamental rethink of how things are done.

Some benchmarking at Salesforce has shown that there's a really
significant penalty in plpgsql functions that have a lot of local
variables, because large "ndatums" increases the size of the
ParamListInfo array that has to be allocated (and zeroed!) for
each expression invocation.

Thinking about this, it struck me that the real problem is that
the parameter list APIs are still too stuck in the last century,
in that they're optimized for static lists of parameters which
is not what plpgsql needs at all.  In fact, if we redefined the
usage of ParamFetchHook so that it would be called on every parameter
fetch, plpgsql could be perfectly happy with just *one* ParamListInfo
slot that it would re-fill each time.  This would entirely eliminate
the need for any per-expression-execution ParamListInfo allocation,
because a function could just use one single-entry ParamListInfo array
for all purposes.

The attached proposed patch represents an exploration of this idea.
I've also attached a SQL script with some simple functions for testing
its performance.  The first one is for exploring what happens with more
or fewer local variables.  I compared current HEAD against the patch
with asserts off, for a call like "SELECT timingcheck(10000000);".
It looks like this (times in ms):

                HEAD    patch

no extra variables        8695    8390
10 extra variables        9218    8419
30 extra variables        9424    8255
100 extra variables        9433    8202

These times are only repeatable to within a few percent, but they do show
that there is a gain rather than loss of performance even in the base
case, and that the patched code's performance is pretty much independent
of the number of local variables whereas HEAD isn't.

The case where the patch potentially loses is where a single expression
contains multiple references to the same plpgsql variable, since with
HEAD, additional references to a variable don't incur any extra trips
through the ParamFetchHook, whereas with the patch they do.  I set up
the reusecheck10() and reusecheck5() functions to see how bad that is.

                HEAD    patch

5 uses of same variable        4105    3962
10 uses of same variable    5738    6076

So somewhere between 5 and 10 uses of the same variable in a single
expression, you start to lose.

I claim that that's a very unlikely scenario and so this patch should
be an unconditional win for just about everybody.

The patch does create an API break for any code that is using
ParamFetchHooks, but it's an easy fix (just return the address of
the array element you stored data into), and any decent C compiler
will complain about the function type mismatch so the API break
should not go unnoticed.

Objections?  Even better ideas?

            regards, tom lane

diff --git a/src/backend/executor/execCurrent.c b/src/backend/executor/execCurrent.c
index 1c8be25..1504c92 100644
*** a/src/backend/executor/execCurrent.c
--- b/src/backend/executor/execCurrent.c
*************** fetch_cursor_param_value(ExprContext *ec
*** 216,226 ****
      if (paramInfo &&
          paramId > 0 && paramId <= paramInfo->numParams)
      {
!         ParamExternData *prm = ¶mInfo->params[paramId - 1];

          /* give hook a chance in case parameter is dynamic */
!         if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
!             (*paramInfo->paramFetch) (paramInfo, paramId);

          if (OidIsValid(prm->ptype) && !prm->isnull)
          {
--- 216,228 ----
      if (paramInfo &&
          paramId > 0 && paramId <= paramInfo->numParams)
      {
!         ParamExternData *prm;

          /* give hook a chance in case parameter is dynamic */
!         if (paramInfo->paramFetch != NULL)
!             prm = (*paramInfo->paramFetch) (paramInfo, paramId, 0);
!         else
!             prm = ¶mInfo->params[paramId - 1];

          if (OidIsValid(prm->ptype) && !prm->isnull)
          {
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d94fe58..f558d40 100644
*** a/src/backend/executor/execQual.c
--- b/src/backend/executor/execQual.c
*************** ExecEvalParamExtern(ExprState *exprstate
*** 1137,1147 ****
      if (paramInfo &&
          thisParamId > 0 && thisParamId <= paramInfo->numParams)
      {
!         ParamExternData *prm = ¶mInfo->params[thisParamId - 1];

          /* give hook a chance in case parameter is dynamic */
!         if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
!             (*paramInfo->paramFetch) (paramInfo, thisParamId);

          if (OidIsValid(prm->ptype))
          {
--- 1137,1149 ----
      if (paramInfo &&
          thisParamId > 0 && thisParamId <= paramInfo->numParams)
      {
!         ParamExternData *prm;

          /* give hook a chance in case parameter is dynamic */
!         if (paramInfo->paramFetch != NULL)
!             prm = (*paramInfo->paramFetch) (paramInfo, thisParamId, 0);
!         else
!             prm = ¶mInfo->params[thisParamId - 1];

          if (OidIsValid(prm->ptype))
          {
diff --git a/src/backend/nodes/params.c b/src/backend/nodes/params.c
index fb803f8..b4916d8 100644
*** a/src/backend/nodes/params.c
--- b/src/backend/nodes/params.c
*************** copyParamList(ParamListInfo from)
*** 52,65 ****

      for (i = 0; i < from->numParams; i++)
      {
!         ParamExternData *oprm = &from->params[i];
          ParamExternData *nprm = &retval->params[i];
          int16        typLen;
          bool        typByVal;

          /* give hook a chance in case parameter is dynamic */
!         if (!OidIsValid(oprm->ptype) && from->paramFetch != NULL)
!             (*from->paramFetch) (from, i + 1);

          /* flat-copy the parameter info */
          *nprm = *oprm;
--- 52,67 ----

      for (i = 0; i < from->numParams; i++)
      {
!         ParamExternData *oprm;
          ParamExternData *nprm = &retval->params[i];
          int16        typLen;
          bool        typByVal;

          /* give hook a chance in case parameter is dynamic */
!         if (from->paramFetch != NULL)
!             oprm = (*from->paramFetch) (from, i + 1, PARAM_CHECK_VALID);
!         else
!             oprm = &from->params[i];

          /* flat-copy the parameter info */
          *nprm = *oprm;
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 84d58ae..c0e086b 100644
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
*************** eval_const_expressions_mutator(Node *nod
*** 2343,2356 ****
          case T_Param:
              {
                  Param       *param = (Param *) node;

                  /* Look to see if we've been given a value for this Param */
                  if (param->paramkind == PARAM_EXTERN &&
!                     context->boundParams != NULL &&
!                     param->paramid > 0 &&
!                     param->paramid <= context->boundParams->numParams)
                  {
!                     ParamExternData *prm = &context->boundParams->params[param->paramid - 1];

                      if (OidIsValid(prm->ptype))
                      {
--- 2343,2365 ----
          case T_Param:
              {
                  Param       *param = (Param *) node;
+                 int            thisParamId = param->paramid;
+                 ParamListInfo paramInfo = context->boundParams;

                  /* Look to see if we've been given a value for this Param */
                  if (param->paramkind == PARAM_EXTERN &&
!                     paramInfo != NULL &&
!                     thisParamId > 0 &&
!                     thisParamId <= paramInfo->numParams)
                  {
!                     ParamExternData *prm;
!
!                     /* give hook a chance in case parameter is dynamic */
!                     if (paramInfo->paramFetch != NULL)
!                         prm = (*paramInfo->paramFetch) (paramInfo, thisParamId,
!                                                         PARAM_CHECK_SAFE);
!                     else
!                         prm = ¶mInfo->params[thisParamId - 1];

                      if (OidIsValid(prm->ptype))
                      {
diff --git a/src/include/nodes/params.h b/src/include/nodes/params.h
index a0f7dd0..268876a 100644
*** a/src/include/nodes/params.h
--- b/src/include/nodes/params.h
*************** struct ParseState;
*** 36,53 ****
   *
   *      There are two hook functions that can be associated with a ParamListInfo
   *      array to support dynamic parameter handling.  First, if paramFetch
!  *      isn't null and the executor requires a value for an invalid parameter
!  *      (one with ptype == InvalidOid), the paramFetch hook is called to give
!  *      it a chance to fill in the parameter value.  Second, a parserSetup
!  *      hook can be supplied to re-instantiate the original parsing hooks if
!  *      a query needs to be re-parsed/planned (as a substitute for supposing
!  *      that the current ptype values represent a fixed set of parameter types).
!
   *      Although the data structure is really an array, not a list, we keep
   *      the old typedef name to avoid unnecessary code changes.
   * ----------------
   */

  #define PARAM_FLAG_CONST    0x0001        /* parameter is constant */

  typedef struct ParamExternData
--- 36,63 ----
   *
   *      There are two hook functions that can be associated with a ParamListInfo
   *      array to support dynamic parameter handling.  First, if paramFetch
!  *      isn't null, the paramFetch hook is called whenever a parameter value
!  *      is required, to give it a chance to fill in the parameter value.
!  *      (If the hook can't supply a value, it should set ptype to InvalidOid to
!  *      specify that the parameter is missing, rather than merely being null.)
!  *      Second, a parserSetup hook can be supplied to re-instantiate the
!  *      original parsing hooks if a query needs to be re-parsed/planned
!  *      (otherwise, we assume that the ptype values represent a fixed
!  *      set of parameter types).
!  *
!  *      Note that if the paramFetch hook is set, the params[] array is
!  *      really entirely virtual and can be of any length, since the hook
!  *      has full control of which entry is accessed.  (As an example,
!  *      PL/pgSQL uses only a single params[] entry that is multiplexed
!  *      among all parameters.)  However, numParams must still be valid
!  *      as it defines the largest possibly-valid parameter number.
!  *
   *      Although the data structure is really an array, not a list, we keep
   *      the old typedef name to avoid unnecessary code changes.
   * ----------------
   */

+ /* flag bits in ParamExternData.pflags: */
  #define PARAM_FLAG_CONST    0x0001        /* parameter is constant */

  typedef struct ParamExternData
*************** typedef struct ParamExternData
*** 60,66 ****

  typedef struct ParamListInfoData *ParamListInfo;

! typedef void (*ParamFetchHook) (ParamListInfo params, int paramid);

  typedef void (*ParserSetupHook) (struct ParseState *pstate, void *arg);

--- 70,81 ----

  typedef struct ParamListInfoData *ParamListInfo;

! /* flag bits for ParamFetchHook's flags argument: */
! #define PARAM_CHECK_VALID    0x0001        /* check paramid validity */
! #define PARAM_CHECK_SAFE    0x0002        /* skip evals that might fail */
!
! typedef ParamExternData *(*ParamFetchHook) (ParamListInfo params, int paramid,
!                                                         int flags);

  typedef void (*ParserSetupHook) (struct ParseState *pstate, void *arg);

*************** typedef struct ParamListInfoData
*** 70,76 ****
      void       *paramFetchArg;
      ParserSetupHook parserSetup;    /* parser setup hook */
      void       *parserSetupArg;
!     int            numParams;        /* number of ParamExternDatas following */
      ParamExternData params[FLEXIBLE_ARRAY_MEMBER];
  }    ParamListInfoData;

--- 85,92 ----
      void       *paramFetchArg;
      ParserSetupHook parserSetup;    /* parser setup hook */
      void       *parserSetupArg;
!     int            numParams;        /* maximum allowable Param number */
!     /* typically, params[] is of length numParams */
      ParamExternData params[FLEXIBLE_ARRAY_MEMBER];
  }    ParamListInfoData;

diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index f24f55a..5b9fe37 100644
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
*************** static int exec_for_query(PLpgSQL_execst
*** 211,217 ****
                 Portal portal, bool prefetch_ok);
  static ParamListInfo setup_param_list(PLpgSQL_execstate *estate,
                   PLpgSQL_expr *expr);
! static void plpgsql_param_fetch(ParamListInfo params, int paramid);
  static void exec_move_row(PLpgSQL_execstate *estate,
                PLpgSQL_rec *rec,
                PLpgSQL_row *row,
--- 211,218 ----
                 Portal portal, bool prefetch_ok);
  static ParamListInfo setup_param_list(PLpgSQL_execstate *estate,
                   PLpgSQL_expr *expr);
! static ParamExternData *plpgsql_param_fetch(ParamListInfo params, int paramid,
!                     int flags);
  static void exec_move_row(PLpgSQL_execstate *estate,
                PLpgSQL_rec *rec,
                PLpgSQL_row *row,
*************** exec_stmt_forc(PLpgSQL_execstate *estate
*** 2197,2206 ****
          elog(ERROR, "could not open cursor: %s",
               SPI_result_code_string(SPI_result));

-     /* don't need paramlist any more */
-     if (paramLI)
-         pfree(paramLI);
-
      /*
       * If cursor variable was NULL, store the generated portal name in it
       */
--- 2198,2203 ----
*************** plpgsql_estate_setup(PLpgSQL_execstate *
*** 3169,3174 ****
--- 3166,3186 ----
      estate->datums = palloc(sizeof(PLpgSQL_datum *) * estate->ndatums);
      /* caller is expected to fill the datums array */

+     /*
+      * Create ParamListInfo with one entry (we only need one because
+      * plpgsql_param_fetch() funnels all requests through entry zero).  This
+      * is shared by all SPI invocations from this estate.  We would just make
+      * this a static part of the estate struct, except that doesn't work for
+      * structs containing flexible arrays.
+      */
+     estate->paramLI = (ParamListInfo)
+         palloc0(offsetof(ParamListInfoData, params) + sizeof(ParamExternData));
+     estate->paramLI->paramFetch = plpgsql_param_fetch;
+     estate->paramLI->paramFetchArg = (void *) estate;
+     estate->paramLI->parserSetup = (ParserSetupHook) plpgsql_parser_setup;
+     estate->paramLI->parserSetupArg = NULL;        /* filled during use */
+     estate->paramLI->numParams = estate->ndatums;
+
      /* set up for use of appropriate simple-expression EState */
      if (simple_eval_estate)
          estate->simple_eval_estate = simple_eval_estate;
*************** plpgsql_estate_setup(PLpgSQL_execstate *
*** 3179,3185 ****
      estate->eval_processed = 0;
      estate->eval_lastoid = InvalidOid;
      estate->eval_econtext = NULL;
-     estate->cur_expr = NULL;

      estate->err_stmt = NULL;
      estate->err_text = NULL;
--- 3191,3196 ----
*************** exec_stmt_execsql(PLpgSQL_execstate *est
*** 3495,3503 ****
                       (rc == SPI_OK_SELECT) ? errhint("If you want to discard the results of a SELECT, use PERFORM
instead."): 0)); 
      }

-     if (paramLI)
-         pfree(paramLI);
-
      return PLPGSQL_RC_OK;
  }

--- 3506,3511 ----
*************** exec_stmt_open(PLpgSQL_execstate *estate
*** 3864,3871 ****

      if (curname)
          pfree(curname);
-     if (paramLI)
-         pfree(paramLI);

      return PLPGSQL_RC_OK;
  }
--- 3872,3877 ----
*************** exec_run_select(PLpgSQL_execstate *estat
*** 4909,4916 ****
          if (*portalP == NULL)
              elog(ERROR, "could not open implicit cursor for query \"%s\": %s",
                   expr->query, SPI_result_code_string(SPI_result));
-         if (paramLI)
-             pfree(paramLI);
          return SPI_OK_CURSOR;
      }

--- 4915,4920 ----
*************** exec_run_select(PLpgSQL_execstate *estat
*** 4930,4938 ****
      estate->eval_processed = SPI_processed;
      estate->eval_lastoid = SPI_lastoid;

-     if (paramLI)
-         pfree(paramLI);
-
      return rc;
  }

--- 4934,4939 ----
*************** exec_eval_simple_expr(PLpgSQL_execstate
*** 5140,5146 ****
      LocalTransactionId curlxid = MyProc->lxid;
      CachedPlan *cplan;
      ParamListInfo paramLI;
-     PLpgSQL_expr *save_cur_expr;
      MemoryContext oldcontext;

      /*
--- 5141,5146 ----
*************** exec_eval_simple_expr(PLpgSQL_execstate
*** 5216,5230 ****
      }

      /*
!      * 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.
!      *
!      * Just for paranoia's sake, save and restore the prior value of
!      * estate->cur_expr, which setup_param_list() sets.
       */
-     save_cur_expr = estate->cur_expr;
-
      paramLI = setup_param_list(estate, expr);
      econtext->ecxt_param_list_info = paramLI;

--- 5216,5223 ----
      }

      /*
!      * Set up param list.
       */
      paramLI = setup_param_list(estate, expr);
      econtext->ecxt_param_list_info = paramLI;

*************** exec_eval_simple_expr(PLpgSQL_execstate
*** 5244,5251 ****
      /* Assorted cleanup */
      expr->expr_simple_in_use = false;

-     estate->cur_expr = save_cur_expr;
-
      if (!estate->readonly_func)
          PopActiveSnapshot();

--- 5237,5242 ----
*************** exec_eval_simple_expr(PLpgSQL_execstate
*** 5266,5285 ****


  /*
!  * Create a ParamListInfo to pass to SPI
!  *
!  * We fill in the values for any expression parameters that are plain
!  * PLpgSQL_var datums; these are cheap and safe to evaluate, and by setting
!  * them with PARAM_FLAG_CONST flags, we allow the planner to use those values
!  * in custom plans.  However, parameters that are not plain PLpgSQL_vars
!  * should not be evaluated here, because they could throw errors (for example
!  * "no such record field") and we do not want that to happen in a part of
!  * the expression that might never be evaluated at runtime.  To handle those
!  * parameters, we set up a paramFetch hook for the executor to call when it
!  * wants a not-presupplied value.
!  *
!  * The result is a locally palloc'd array that should be pfree'd after use;
!  * but note it can be NULL.
   */
  static ParamListInfo
  setup_param_list(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
--- 5257,5263 ----


  /*
!  * Set up a ParamListInfo to pass to SPI for this expression
   */
  static ParamListInfo
  setup_param_list(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
*************** setup_param_list(PLpgSQL_execstate *esta
*** 5292,5344 ****
       */
      Assert(expr->plan != NULL);

!     /*
!      * Could we re-use these arrays instead of palloc'ing a new one each time?
!      * However, we'd have to re-fill the array each time anyway, since new
!      * values might have been assigned to the variables.
!      */
      if (!bms_is_empty(expr->paramnos))
      {
!         int            dno;

!         paramLI = (ParamListInfo)
!             palloc0(offsetof(ParamListInfoData, params) +
!                     estate->ndatums * sizeof(ParamExternData));
!         paramLI->paramFetch = plpgsql_param_fetch;
!         paramLI->paramFetchArg = (void *) estate;
!         paramLI->parserSetup = (ParserSetupHook) plpgsql_parser_setup;
          paramLI->parserSetupArg = (void *) expr;
-         paramLI->numParams = estate->ndatums;
-
-         /* Instantiate values for "safe" parameters of the expression */
-         dno = -1;
-         while ((dno = bms_next_member(expr->paramnos, dno)) >= 0)
-         {
-             PLpgSQL_datum *datum = estate->datums[dno];
-
-             if (datum->dtype == PLPGSQL_DTYPE_VAR)
-             {
-                 PLpgSQL_var *var = (PLpgSQL_var *) datum;
-                 ParamExternData *prm = ¶mLI->params[dno];
-
-                 prm->value = var->value;
-                 prm->isnull = var->isnull;
-                 prm->pflags = PARAM_FLAG_CONST;
-                 prm->ptype = var->datatype->typoid;
-             }
-         }

          /*
!          * 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.
!          */
!         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.  (Should be set already, but let's be sure.)
           */
          expr->func = estate->func;
      }
--- 5270,5287 ----
       */
      Assert(expr->plan != NULL);

!     /* We only need a ParamListInfo if the expression has parameters */
      if (!bms_is_empty(expr->paramnos))
      {
!         /* Use the common ParamListInfo for all evals in this estate */
!         paramLI = estate->paramLI;

!         /* Make sure it points to this expr while we use it */
          paramLI->parserSetupArg = (void *) expr;

          /*
!          * Make sure this is set before parser hooks need it.  (Should be set
!          * already, but let's be sure.)
           */
          expr->func = estate->func;
      }
*************** setup_param_list(PLpgSQL_execstate *esta
*** 5357,5370 ****
  /*
   * plpgsql_param_fetch        paramFetch callback for dynamic parameter fetch
   */
! static void
! plpgsql_param_fetch(ParamListInfo params, int paramid)
  {
      int            dno;
      PLpgSQL_execstate *estate;
      PLpgSQL_expr *expr;
      PLpgSQL_datum *datum;
-     ParamExternData *prm;
      int32        prmtypmod;

      /* paramid's are 1-based, but dnos are 0-based */
--- 5300,5313 ----
  /*
   * plpgsql_param_fetch        paramFetch callback for dynamic parameter fetch
   */
! static ParamExternData *
! plpgsql_param_fetch(ParamListInfo params, int paramid, int flags)
  {
+     ParamExternData *prm = ¶ms->params[0];
      int            dno;
      PLpgSQL_execstate *estate;
      PLpgSQL_expr *expr;
      PLpgSQL_datum *datum;
      int32        prmtypmod;

      /* paramid's are 1-based, but dnos are 0-based */
*************** plpgsql_param_fetch(ParamListInfo params
*** 5373,5395 ****

      /* fetch back the hook data */
      estate = (PLpgSQL_execstate *) params->paramFetchArg;
!     expr = estate->cur_expr;
      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.
       */
!     if (!bms_is_member(dno, expr->paramnos))
!         return;

!     /* OK, evaluate the value and store into the appropriate paramlist slot */
!     datum = estate->datums[dno];
!     prm = ¶ms->params[dno];
      exec_eval_datum(estate, datum,
                      &prm->ptype, &prmtypmod,
                      &prm->value, &prm->isnull);
  }


--- 5316,5361 ----

      /* fetch back the hook data */
      estate = (PLpgSQL_execstate *) params->paramFetchArg;
!     expr = (PLpgSQL_expr *) params->parserSetupArg;
      Assert(params->numParams == estate->ndatums);

+     /* now we can look up the datum */
+     datum = estate->datums[dno];
+
      /*
!      * We return null/invalid in two cases:
!      *
!      * 1. If PARAM_CHECK_VALID is set and we're 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 parameter slots.
!      *
!      * 2. If PARAM_CHECK_SAFE is set and we're not sure evaluation will
!      * succeed; the caller would rather we not return a value than cause an
!      * unexpected error.
       */
!     if (flags)
!     {
!         if (((flags & PARAM_CHECK_VALID) &&
!              !bms_is_member(dno, expr->paramnos)) ||
!             ((flags & PARAM_CHECK_SAFE) &&
!              datum->dtype != PLPGSQL_DTYPE_VAR))
!         {
!             prm->value = (Datum) 0;
!             prm->isnull = true;
!             prm->pflags = 0;
!             prm->ptype = InvalidOid;
!             return prm;
!         }
!     }

!     /* OK, evaluate the value and store into the single paramlist slot */
      exec_eval_datum(estate, datum,
                      &prm->ptype, &prmtypmod,
                      &prm->value, &prm->isnull);
+     /* If we're being called by the planner, OK to treat value as constant */
+     prm->pflags = PARAM_FLAG_CONST;
+     return prm;
  }


diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 4ec4628..032f283 100644
*** a/src/pl/plpgsql/src/plpgsql.h
--- b/src/pl/plpgsql/src/plpgsql.h
*************** typedef struct PLpgSQL_execstate
*** 784,789 ****
--- 784,792 ----
      int            ndatums;
      PLpgSQL_datum **datums;

+     /* Struct for passing datum values to executor */
+     ParamListInfo paramLI;
+
      /* EState to use for "simple" expression evaluation */
      EState       *simple_eval_estate;

*************** typedef struct PLpgSQL_execstate
*** 792,798 ****
      uint32        eval_processed;
      Oid            eval_lastoid;
      ExprContext *eval_econtext; /* for executing simple expressions */
-     PLpgSQL_expr *cur_expr;        /* current query/expr being evaluated */

      /* status information for error context reporting */
      PLpgSQL_stmt *err_stmt;        /* current stmt */
--- 795,800 ----
create or replace function timingcheck(n int) returns bigint as $$
declare arr int[] := '{}';
  res bigint := 0;
  dummyvar0 int := 0;
  dummyvar1 int := 0;
  dummyvar2 int := 0;
  dummyvar3 int := 0;
  dummyvar4 int := 0;
  dummyvar5 int := 0;
  dummyvar6 int := 0;
  dummyvar7 int := 0;
  dummyvar8 int := 0;
  dummyvar9 int := 0;
/*
  dummyvar10 int := 0;
  dummyvar11 int := 0;
  dummyvar12 int := 0;
  dummyvar13 int := 0;
  dummyvar14 int := 0;
  dummyvar15 int := 0;
  dummyvar16 int := 0;
  dummyvar17 int := 0;
  dummyvar18 int := 0;
  dummyvar19 int := 0;
  dummyvar20 int := 0;
  dummyvar21 int := 0;
  dummyvar22 int := 0;
  dummyvar23 int := 0;
  dummyvar24 int := 0;
  dummyvar25 int := 0;
  dummyvar26 int := 0;
  dummyvar27 int := 0;
  dummyvar28 int := 0;
  dummyvar29 int := 0;
  dummyvar30 int := 0;
  dummyvar31 int := 0;
  dummyvar32 int := 0;
  dummyvar33 int := 0;
  dummyvar34 int := 0;
  dummyvar35 int := 0;
  dummyvar36 int := 0;
  dummyvar37 int := 0;
  dummyvar38 int := 0;
  dummyvar39 int := 0;
  dummyvar40 int := 0;
  dummyvar41 int := 0;
  dummyvar42 int := 0;
  dummyvar43 int := 0;
  dummyvar44 int := 0;
  dummyvar45 int := 0;
  dummyvar46 int := 0;
  dummyvar47 int := 0;
  dummyvar48 int := 0;
  dummyvar49 int := 0;
  dummyvar50 int := 0;
  dummyvar51 int := 0;
  dummyvar52 int := 0;
  dummyvar53 int := 0;
  dummyvar54 int := 0;
  dummyvar55 int := 0;
  dummyvar56 int := 0;
  dummyvar57 int := 0;
  dummyvar58 int := 0;
  dummyvar59 int := 0;
  dummyvar60 int := 0;
  dummyvar61 int := 0;
  dummyvar62 int := 0;
  dummyvar63 int := 0;
  dummyvar64 int := 0;
  dummyvar65 int := 0;
  dummyvar66 int := 0;
  dummyvar67 int := 0;
  dummyvar68 int := 0;
  dummyvar69 int := 0;
  dummyvar70 int := 0;
  dummyvar71 int := 0;
  dummyvar72 int := 0;
  dummyvar73 int := 0;
  dummyvar74 int := 0;
  dummyvar75 int := 0;
  dummyvar76 int := 0;
  dummyvar77 int := 0;
  dummyvar78 int := 0;
  dummyvar79 int := 0;
  dummyvar80 int := 0;
  dummyvar81 int := 0;
  dummyvar82 int := 0;
  dummyvar83 int := 0;
  dummyvar84 int := 0;
  dummyvar85 int := 0;
  dummyvar86 int := 0;
  dummyvar87 int := 0;
  dummyvar88 int := 0;
  dummyvar89 int := 0;
  dummyvar90 int := 0;
  dummyvar91 int := 0;
  dummyvar92 int := 0;
  dummyvar93 int := 0;
  dummyvar94 int := 0;
  dummyvar95 int := 0;
  dummyvar96 int := 0;
  dummyvar97 int := 0;
  dummyvar98 int := 0;
  dummyvar99 int := 0;
*/
begin
  for i in 1 .. n loop
    arr[10] := i;
    res := res + arr[10];
  end loop;
  return res;
end
$$ language plpgsql strict stable;

create or replace function reusecheck10(n int) returns bigint as $$
declare res bigint := 0;
begin
  for i in 1 .. n loop
    res := res + i + i + i + i + i + i + i + i + i + i;
  end loop;
  return res;
end
$$ language plpgsql strict stable;

create or replace function reusecheck5(n int) returns bigint as $$
declare res bigint := 0;
begin
  for i in 1 .. n loop
    res := res + i + i + i + i + i;
  end loop;
  return res;
end
$$ language plpgsql strict stable;

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Andrew Dunstan
Дата:
Сообщение: Re: Bootstrap DATA is a pita
Следующее
От: Ashutosh Bapat
Дата:
Сообщение: Re: Join push-down support for foreign tables