*** src/backend/commands/explain.c.orig Tue Aug 1 21:59:45 2006 --- src/backend/commands/explain.c Wed Sep 6 12:28:15 2006 *************** *** 191,197 **** } /* plan the query */ ! plan = planner(query, isCursor, cursorOptions, NULL); /* * Update snapshot command ID to ensure this query sees results of any --- 191,197 ---- } /* plan the query */ ! plan = planner(query, isCursor, cursorOptions, params); /* * Update snapshot command ID to ensure this query sees results of any *** src/backend/commands/portalcmds.c.orig Sun Sep 3 13:44:27 2006 --- src/backend/commands/portalcmds.c Wed Sep 6 12:28:15 2006 *************** *** 95,101 **** errmsg("DECLARE CURSOR ... FOR UPDATE/SHARE is not supported"), errdetail("Cursors must be READ ONLY."))); ! plan = planner(query, true, stmt->options, NULL); /* * Create a portal and copy the query and plan into its memory context. --- 95,101 ---- errmsg("DECLARE CURSOR ... FOR UPDATE/SHARE is not supported"), errdetail("Cursors must be READ ONLY."))); ! plan = planner(query, true, stmt->options, params); /* * Create a portal and copy the query and plan into its memory context. *** src/backend/commands/prepare.c.orig Wed Aug 30 13:02:43 2006 --- src/backend/commands/prepare.c Wed Sep 6 11:22:11 2006 *************** *** 261,266 **** --- 261,267 ---- ParamExternData *prm = ¶mLI->params[i]; prm->ptype = lfirst_oid(la); + prm->pflags = 0; prm->value = ExecEvalExprSwitchContext(n, GetPerTupleExprContext(estate), &prm->isnull, *** src/backend/executor/functions.c.orig Sat Aug 12 16:05:55 2006 --- src/backend/executor/functions.c Wed Sep 6 11:22:20 2006 *************** *** 442,447 **** --- 442,448 ---- prm->value = fcinfo->arg[i]; prm->isnull = fcinfo->argnull[i]; + prm->pflags = 0; prm->ptype = fcache->argtypes[i]; } } *** src/backend/executor/spi.c.orig Sun Sep 3 13:44:27 2006 --- src/backend/executor/spi.c Wed Sep 6 11:22:20 2006 *************** *** 893,898 **** --- 893,899 ---- ParamExternData *prm = ¶mLI->params[k]; prm->ptype = spiplan->argtypes[k]; + prm->pflags = 0; prm->isnull = (Nulls && Nulls[k] == 'n'); if (prm->isnull) { *************** *** 1357,1362 **** --- 1358,1364 ---- prm->value = Values[k]; prm->isnull = (Nulls && Nulls[k] == 'n'); + prm->pflags = 0; prm->ptype = plan->argtypes[k]; } } *** src/backend/optimizer/util/clauses.c.orig Sat Aug 12 16:05:55 2006 --- src/backend/optimizer/util/clauses.c Wed Sep 6 12:04:03 2006 *************** *** 1462,1468 **** * * Currently the extra steps that are taken in this mode are: * 1. Substitute values for Params, where a bound Param value has been made ! * available by the caller of planner(). * 2. Fold stable, as well as immutable, functions to constants. *-------------------- */ --- 1462,1470 ---- * * Currently the extra steps that are taken in this mode are: * 1. Substitute values for Params, where a bound Param value has been made ! * available by the caller of planner(), even if the Param isn't marked ! * constant. This effectively means that we plan using the first supplied ! * value of the Param. * 2. Fold stable, as well as immutable, functions to constants. *-------------------- */ *************** *** 1487,1519 **** { Param *param = (Param *) node; ! /* OK to try to substitute value? */ ! if (context->estimate && param->paramkind == PARAM_EXTERN && ! PlannerBoundParamList != NULL) { ! /* Look to see if we've been given a value for this Param */ ! if (param->paramid > 0 && ! param->paramid <= PlannerBoundParamList->numParams) ! { ! ParamExternData *prm = &PlannerBoundParamList->params[param->paramid - 1]; ! if (OidIsValid(prm->ptype)) { /* ! * Found it, so return a Const representing the param ! * value. Note that we don't copy pass-by-ref datatypes, ! * so the Const will only be valid as long as the bound ! * parameter list exists. This is okay for intended uses ! * of estimate_expression_value(). */ int16 typLen; bool typByVal; Assert(prm->ptype == param->paramtype); get_typlenbyval(param->paramtype, &typLen, &typByVal); return (Node *) makeConst(param->paramtype, (int) typLen, ! prm->value, prm->isnull, typByVal); } --- 1489,1526 ---- { Param *param = (Param *) node; ! /* Look to see if we've been given a value for this Param */ ! if (param->paramkind == PARAM_EXTERN && ! PlannerBoundParamList != NULL && ! param->paramid > 0 && ! param->paramid <= PlannerBoundParamList->numParams) { ! ParamExternData *prm = &PlannerBoundParamList->params[param->paramid - 1]; ! if (OidIsValid(prm->ptype)) ! { ! /* OK to substitute parameter value? */ ! if (context->estimate || (prm->pflags & PARAM_FLAG_CONST)) { /* ! * Return a Const representing the param value. Must copy ! * pass-by-ref datatypes, since the Param might be in a ! * memory context shorter-lived than our output plan ! * should be. */ int16 typLen; bool typByVal; + Datum pval; Assert(prm->ptype == param->paramtype); get_typlenbyval(param->paramtype, &typLen, &typByVal); + if (prm->isnull || typByVal) + pval = prm->value; + else + pval = datumCopy(prm->value, typByVal, typLen); return (Node *) makeConst(param->paramtype, (int) typLen, ! pval, prm->isnull, typByVal); } *** src/backend/tcop/postgres.c.orig Sun Sep 3 13:44:33 2006 --- src/backend/tcop/postgres.c Wed Sep 6 11:52:42 2006 *************** *** 1168,1174 **** * statement, we assume the statement isn't going to hang around long, so * getting rid of temp space quickly is probably not worth the costs of * copying parse/plan trees. So in this case, we set up a special context ! * for the unnamed statement, and do all the parsing/planning therein. */ is_named = (stmt_name[0] != '\0'); if (is_named) --- 1168,1174 ---- * statement, we assume the statement isn't going to hang around long, so * getting rid of temp space quickly is probably not worth the costs of * copying parse/plan trees. So in this case, we set up a special context ! * for the unnamed statement, and do all the parsing work therein. */ is_named = (stmt_name[0] != '\0'); if (is_named) *************** *** 1367,1372 **** --- 1367,1373 ---- PreparedStatement *pstmt; Portal portal; ParamListInfo params; + List *plan_list; StringInfoData bind_values_str; pgstat_report_activity(""); *************** *** 1474,1479 **** --- 1475,1481 ---- { Oid ptype = lfirst_oid(l); int32 plength; + Datum pval; bool isNull; StringInfoData pbuf; char csave; *************** *** 1532,1542 **** else pstring = pg_client_to_server(pbuf.data, plength); ! params->params[paramno].value = ! OidInputFunctionCall(typinput, ! pstring, ! typioparam, ! -1); /* Save the parameter values */ appendStringInfo(&bind_values_str, "%s$%d = ", --- 1534,1540 ---- else pstring = pg_client_to_server(pbuf.data, plength); ! pval = OidInputFunctionCall(typinput, pstring, typioparam, -1); /* Save the parameter values */ appendStringInfo(&bind_values_str, "%s$%d = ", *************** *** 1576,1585 **** else bufptr = &pbuf; ! params->params[paramno].value = OidReceiveFunctionCall(typreceive, ! bufptr, ! typioparam, ! -1); /* Trouble if it didn't eat the whole buffer */ if (!isNull && pbuf.cursor != pbuf.len) --- 1574,1580 ---- else bufptr = &pbuf; ! pval = OidReceiveFunctionCall(typreceive, bufptr, typioparam, -1); /* Trouble if it didn't eat the whole buffer */ if (!isNull && pbuf.cursor != pbuf.len) *************** *** 1596,1608 **** --- 1591,1612 ---- (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unsupported format code: %d", pformat))); + pval = 0; /* keep compiler quiet */ } /* Restore message buffer contents */ if (!isNull) pbuf.data[plength] = csave; + params->params[paramno].value = pval; params->params[paramno].isnull = isNull; + /* + * We mark the params as CONST. This has no effect if we already + * did planning, but if we didn't, it licenses the planner to + * substitute the parameters directly into the one-shot plan we + * will generate below. + */ + params->params[paramno].pflags = PARAM_FLAG_CONST; params->params[paramno].ptype = ptype; paramno++; *************** *** 1641,1659 **** /* * If we didn't plan the query before, do it now. This allows the planner ! * to make use of the concrete parameter values we now have. * ! * This happens only for unnamed statements, and so switching into the ! * statement context for planning is correct (see notes in ! * exec_parse_message). */ if (pstmt->plan_list == NIL && pstmt->query_list != NIL) { ! MemoryContext oldContext = MemoryContextSwitchTo(pstmt->context); ! pstmt->plan_list = pg_plan_queries(pstmt->query_list, params, true); MemoryContextSwitchTo(oldContext); } /* * Define portal and start execution. --- 1645,1673 ---- /* * If we didn't plan the query before, do it now. This allows the planner ! * to make use of the concrete parameter values we now have. Because we ! * use PARAM_FLAG_CONST, the plan is good only for this set of param ! * values, and so we generate the plan in the portal's own memory context ! * where it will be thrown away after use. As in exec_parse_message, ! * we make no attempt to recover planner temporary memory until the end ! * of the operation. * ! * XXX because the planner has a bad habit of scribbling on its input, ! * we have to make a copy of the parse trees, just in case someone binds ! * and executes an unnamed statement multiple times. FIXME someday */ if (pstmt->plan_list == NIL && pstmt->query_list != NIL) { ! MemoryContext oldContext; ! oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); ! plan_list = pg_plan_queries(copyObject(pstmt->query_list), ! params, ! true); MemoryContextSwitchTo(oldContext); } + else + plan_list = pstmt->plan_list; /* * Define portal and start execution. *************** *** 1664,1670 **** bind_values_str.len ? pstrdup(bind_values_str.data) : NULL, pstmt->commandTag, pstmt->query_list, ! pstmt->plan_list, pstmt->context); PortalStart(portal, params, InvalidSnapshot); --- 1678,1684 ---- bind_values_str.len ? pstrdup(bind_values_str.data) : NULL, pstmt->commandTag, pstmt->query_list, ! plan_list, pstmt->context); PortalStart(portal, params, InvalidSnapshot); *** src/include/nodes/params.h.orig Fri Apr 21 21:26:01 2006 --- src/include/nodes/params.h Wed Sep 6 11:12:38 2006 *************** *** 26,40 **** --- 26,47 ---- * Although parameter numbers are normally consecutive, we allow * ptype == InvalidOid to signal an unused array entry. * + * PARAM_FLAG_CONST signals the planner that it may treat this parameter + * as a constant (i.e., generate a plan that works only for this value + * of the parameter). + * * 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 { Datum value; /* parameter value */ bool isnull; /* is it NULL? */ + uint16 pflags; /* flag bits, see above */ Oid ptype; /* parameter's datatype, or 0 */ } ParamExternData; *** src/pl/plpgsql/src/pl_exec.c.orig Sun Aug 27 19:54:46 2006 --- src/pl/plpgsql/src/pl_exec.c Wed Sep 6 11:22:46 2006 *************** *** 3958,3963 **** --- 3958,3964 ---- ParamExternData *prm = ¶mLI->params[i]; PLpgSQL_datum *datum = estate->datums[expr->params[i]]; + prm->pflags = 0; exec_eval_datum(estate, datum, expr->plan_argtypes[i], &prm->ptype, &prm->value, &prm->isnull);