Commit 9bedd128 authored by Tom Lane's avatar Tom Lane

Add support for invoking parser callback hooks via SPI and in cached plans.

As proof of concept, modify plpgsql to use the hooks.  plpgsql is still
inserting $n symbols textually, but the "back end" of the parsing process now
goes through the ParamRef hook instead of using a fixed parameter-type array,
and then execution only fetches actually-referenced parameters, using a hook
added to ParamListInfo.

Although there's a lot left to be done in plpgsql, this already cures the
"if (TG_OP = 'INSERT' and NEW.foo ...)"  problem, as illustrated by the
changed regression test.
parent 48912acc
This diff is collapsed.
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.192 2009/10/12 18:10:41 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.193 2009/11/04 22:26:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -107,8 +107,6 @@ ExplainQuery(ExplainStmt *stmt, const char *queryString,
ParamListInfo params, DestReceiver *dest)
{
ExplainState es;
Oid *param_types;
int num_params;
TupOutputState *tstate;
List *rewritten;
ListCell *lc;
......@@ -150,9 +148,6 @@ ExplainQuery(ExplainStmt *stmt, const char *queryString,
opt->defname)));
}
/* Convert parameter type data to the form parser wants */
getParamListTypes(params, &param_types, &num_params);
/*
* Run parse analysis and rewrite. Note this also acquires sufficient
* locks on the source table(s).
......@@ -163,8 +158,10 @@ ExplainQuery(ExplainStmt *stmt, const char *queryString,
* executed repeatedly. (See also the same hack in DECLARE CURSOR and
* PREPARE.) XXX FIXME someday.
*/
rewritten = pg_analyze_and_rewrite((Node *) copyObject(stmt->query),
queryString, param_types, num_params);
rewritten = pg_analyze_and_rewrite_params((Node *) copyObject(stmt->query),
queryString,
(ParserSetupHook) setupParserWithParamList,
params);
/* emit opening boilerplate */
ExplainBeginOutput(&es);
......
......@@ -10,7 +10,7 @@
* Copyright (c) 2002-2009, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.99 2009/08/10 05:46:50 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.100 2009/11/04 22:26:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -379,6 +379,11 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
paramLI = (ParamListInfo)
palloc(sizeof(ParamListInfoData) +
(num_params - 1) *sizeof(ParamExternData));
/* we have static list of params, so no hooks needed */
paramLI->paramFetch = NULL;
paramLI->paramFetchArg = NULL;
paramLI->parserSetup = NULL;
paramLI->parserSetupArg = NULL;
paramLI->numParams = num_params;
i = 0;
......
......@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/backend/executor/execCurrent.c,v 1.12 2009/10/26 02:26:29 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/execCurrent.c,v 1.13 2009/11/04 22:26:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -217,9 +217,21 @@ fetch_param_value(ExprContext *econtext, int paramId)
{
ParamExternData *prm = &paramInfo->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)
{
Assert(prm->ptype == REFCURSOROID);
/* safety check in case hook did something unexpected */
if (prm->ptype != REFCURSOROID)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
paramId,
format_type_be(prm->ptype),
format_type_be(REFCURSOROID))));
/* We know that refcursor uses text's I/O routines */
return TextDatumGetCString(prm->value);
}
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.253 2009/10/26 02:26:29 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.254 2009/11/04 22:26:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -882,9 +882,21 @@ ExecEvalParam(ExprState *exprstate, ExprContext *econtext,
{
ParamExternData *prm = &paramInfo->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))
{
Assert(prm->ptype == expression->paramtype);
/* safety check in case hook did something unexpected */
if (prm->ptype != expression->paramtype)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
thisParamId,
format_type_be(prm->ptype),
format_type_be(expression->paramtype))));
*isNull = prm->isnull;
return prm->value;
}
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.135 2009/06/11 17:25:38 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.136 2009/11/04 22:26:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -526,6 +526,11 @@ postquel_sub_params(SQLFunctionCachePtr fcache,
/* sizeof(ParamListInfoData) includes the first array element */
paramLI = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
(nargs - 1) *sizeof(ParamExternData));
/* we have static list of params, so no hooks needed */
paramLI->paramFetch = NULL;
paramLI->paramFetchArg = NULL;
paramLI->parserSetup = NULL;
paramLI->parserSetupArg = NULL;
paramLI->numParams = nargs;
fcache->paramLI = paramLI;
}
......
This diff is collapsed.
......@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/params.c,v 1.11 2009/01/01 17:23:43 momjian Exp $
* $PostgreSQL: pgsql/src/backend/nodes/params.c,v 1.12 2009/11/04 22:26:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -16,6 +16,7 @@
#include "postgres.h"
#include "nodes/params.h"
#include "parser/parse_param.h"
#include "utils/datum.h"
#include "utils/lsyscache.h"
......@@ -24,6 +25,11 @@
* Copy a ParamListInfo structure.
*
* The result is allocated in CurrentMemoryContext.
*
* Note: the intent of this function is to make a static, self-contained
* set of parameter values. If dynamic parameter hooks are present, we
* intentionally do not copy them into the result. Rather, we forcibly
* instantiate all available parameter values and copy the datum values.
*/
ParamListInfo
copyParamList(ParamListInfo from)
......@@ -40,54 +46,76 @@ copyParamList(ParamListInfo from)
(from->numParams - 1) *sizeof(ParamExternData);
retval = (ParamListInfo) palloc(size);
memcpy(retval, from, size);
retval->paramFetch = NULL;
retval->paramFetchArg = NULL;
retval->parserSetup = NULL;
retval->parserSetupArg = NULL;
retval->numParams = from->numParams;
/*
* Flat-copy is not good enough for pass-by-ref data values, so make a
* pass over the array to copy those.
*/
for (i = 0; i < retval->numParams; i++)
for (i = 0; i < from->numParams; i++)
{
ParamExternData *prm = &retval->params[i];
ParamExternData *oprm = &from->params[i];
ParamExternData *nprm = &retval->params[i];
int16 typLen;
bool typByVal;
if (prm->isnull || !OidIsValid(prm->ptype))
/* 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;
/* need datumCopy in case it's a pass-by-reference datatype */
if (nprm->isnull || !OidIsValid(nprm->ptype))
continue;
get_typlenbyval(prm->ptype, &typLen, &typByVal);
prm->value = datumCopy(prm->value, typByVal, typLen);
get_typlenbyval(nprm->ptype, &typLen, &typByVal);
nprm->value = datumCopy(nprm->value, typByVal, typLen);
}
return retval;
}
/*
* Extract an array of parameter type OIDs from a ParamListInfo.
* Set up the parser to treat the given list of run-time parameters
* as available external parameters during parsing of a new query.
*
* The result is allocated in CurrentMemoryContext.
* Note that the parser doesn't actually care about the *values* of the given
* parameters, only about their *types*. Also, the code that originally
* provided the ParamListInfo may have provided a setupHook, which should
* override applying parse_fixed_parameters().
*/
void
getParamListTypes(ParamListInfo params,
Oid **param_types, int *num_params)
setupParserWithParamList(struct ParseState *pstate,
ParamListInfo params)
{
Oid *ptypes;
int i;
if (params == NULL) /* no params, nothing to do */
return;
if (params == NULL || params->numParams <= 0)
/* If there is a parserSetup hook, it gets to do this */
if (params->parserSetup != NULL)
{
*param_types = NULL;
*num_params = 0;
(*params->parserSetup) (pstate, params->parserSetupArg);
return;
}
ptypes = (Oid *) palloc(params->numParams * sizeof(Oid));
*param_types = ptypes;
*num_params = params->numParams;
for (i = 0; i < params->numParams; i++)
/* Else, treat any available parameters as being of fixed type */
if (params->numParams > 0)
{
ParamExternData *prm = &params->params[i];
Oid *ptypes;
int i;
ptypes = (Oid *) palloc(params->numParams * sizeof(Oid));
for (i = 0; i < params->numParams; i++)
{
ParamExternData *prm = &params->params[i];
/* give hook a chance in case parameter is dynamic */
if (!OidIsValid(prm->ptype) && params->paramFetch != NULL)
(*params->paramFetch) (params, i+1);
ptypes[i] = prm->ptype;
ptypes[i] = prm->ptype;
}
parse_fixed_parameters(pstate, ptypes, params->numParams);
}
}
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.574 2009/10/08 22:34:57 tgl Exp $
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.575 2009/11/04 22:26:06 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
......@@ -627,6 +627,52 @@ pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
return querytree_list;
}
/*
* Do parse analysis and rewriting. This is the same as pg_analyze_and_rewrite
* except that external-parameter resolution is determined by parser callback
* hooks instead of a fixed list of parameter datatypes.
*/
List *
pg_analyze_and_rewrite_params(Node *parsetree,
const char *query_string,
ParserSetupHook parserSetup,
void *parserSetupArg)
{
ParseState *pstate;
Query *query;
List *querytree_list;
Assert(query_string != NULL); /* required as of 8.4 */
TRACE_POSTGRESQL_QUERY_REWRITE_START(query_string);
/*
* (1) Perform parse analysis.
*/
if (log_parser_stats)
ResetUsage();
pstate = make_parsestate(NULL);
pstate->p_sourcetext = query_string;
(*parserSetup) (pstate, parserSetupArg);
query = transformStmt(pstate, parsetree);
free_parsestate(pstate);
if (log_parser_stats)
ShowUsage("PARSE ANALYSIS STATISTICS");
/*
* (2) Rewrite the queries, as necessary
*/
querytree_list = pg_rewrite_query(query);
TRACE_POSTGRESQL_QUERY_REWRITE_DONE(query_string);
return querytree_list;
}
/*
* Perform rewriting of a query produced by parse analysis.
*
......@@ -1536,6 +1582,11 @@ exec_bind_message(StringInfo input_message)
/* sizeof(ParamListInfoData) includes the first array element */
params = (ParamListInfo) palloc(sizeof(ParamListInfoData) +
(numParams - 1) *sizeof(ParamExternData));
/* we have static list of params, so no hooks needed */
params->paramFetch = NULL;
params->paramFetchArg = NULL;
params->parserSetup = NULL;
params->parserSetupArg = NULL;
params->numParams = numParams;
for (paramno = 0; paramno < numParams; paramno++)
......
......@@ -35,7 +35,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.30 2009/10/26 02:26:41 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.31 2009/11/04 22:26:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -99,8 +99,8 @@ InitPlanCache(void)
* raw_parse_tree: output of raw_parser()
* query_string: original query text (as of PG 8.4, must not be NULL)
* commandTag: compile-time-constant tag for query, or NULL if empty query
* param_types: array of parameter type OIDs, or NULL if none
* num_params: number of parameters
* param_types: array of fixed parameter type OIDs, or NULL if none
* num_params: number of fixed parameters
* cursor_options: options bitmask that was/will be passed to planner
* stmt_list: list of PlannedStmts/utility stmts, or list of Query trees
* fully_planned: are we caching planner or rewriter output?
......@@ -156,6 +156,9 @@ CreateCachedPlan(Node *raw_parse_tree,
else
plansource->param_types = NULL;
plansource->num_params = num_params;
/* these can be set later with CachedPlanSetParserHook: */
plansource->parserSetup = NULL;
plansource->parserSetupArg = NULL;
plansource->cursor_options = cursor_options;
plansource->fully_planned = fully_planned;
plansource->fixed_result = fixed_result;
......@@ -240,6 +243,9 @@ FastCreateCachedPlan(Node *raw_parse_tree,
plansource->commandTag = commandTag; /* no copying needed */
plansource->param_types = param_types;
plansource->num_params = num_params;
/* these can be set later with CachedPlanSetParserHook: */
plansource->parserSetup = NULL;
plansource->parserSetupArg = NULL;
plansource->cursor_options = cursor_options;
plansource->fully_planned = fully_planned;
plansource->fixed_result = fixed_result;
......@@ -274,6 +280,27 @@ FastCreateCachedPlan(Node *raw_parse_tree,
return plansource;
}
/*
* CachedPlanSetParserHook: set up to use parser callback hooks
*
* Use this when a caller wants to manage parameter information via parser
* callbacks rather than a fixed parameter-types list. Beware that the
* information pointed to by parserSetupArg must be valid for as long as
* the cached plan might be replanned!
*/
void
CachedPlanSetParserHook(CachedPlanSource *plansource,
ParserSetupHook parserSetup,
void *parserSetupArg)
{
/* Must not have specified a fixed parameter-types list */
Assert(plansource->param_types == NULL);
Assert(plansource->num_params == 0);
/* OK, save hook info */
plansource->parserSetup = parserSetup;
plansource->parserSetupArg = parserSetupArg;
}
/*
* StoreCachedPlan: store a built or rebuilt plan into a plancache entry.
*
......@@ -466,6 +493,7 @@ RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner)
if (!plan)
{
bool snapshot_set = false;
Node *rawtree;
List *slist;
TupleDesc resultDesc;
......@@ -491,14 +519,19 @@ RevalidateCachedPlan(CachedPlanSource *plansource, bool useResOwner)
/*
* Run parse analysis and rule rewriting. The parser tends to
* scribble on its input, so we must copy the raw parse tree to
* prevent corruption of the cache. Note that we do not use
* parse_analyze_varparams(), assuming that the caller never wants the
* parameter types to change from the original values.
* prevent corruption of the cache.
*/
slist = pg_analyze_and_rewrite(copyObject(plansource->raw_parse_tree),
plansource->query_string,
plansource->param_types,
plansource->num_params);
rawtree = copyObject(plansource->raw_parse_tree);
if (plansource->parserSetup != NULL)
slist = pg_analyze_and_rewrite_params(rawtree,
plansource->query_string,
plansource->parserSetup,
plansource->parserSetupArg);
else
slist = pg_analyze_and_rewrite(rawtree,
plansource->query_string,
plansource->param_types,
plansource->num_params);
if (plansource->fully_planned)
{
......
......@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.72 2009/06/11 14:49:11 momjian Exp $
* $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.73 2009/11/04 22:26:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -73,6 +73,9 @@ extern void SPI_restore_connection(void);
extern int SPI_execute(const char *src, bool read_only, long tcount);
extern int SPI_execute_plan(SPIPlanPtr plan, Datum *Values, const char *Nulls,
bool read_only, long tcount);
extern int SPI_execute_plan_with_paramlist(SPIPlanPtr plan,
ParamListInfo params,
bool read_only, long tcount);
extern int SPI_exec(const char *src, long tcount);
extern int SPI_execp(SPIPlanPtr plan, Datum *Values, const char *Nulls,
long tcount);
......@@ -88,6 +91,10 @@ extern int SPI_execute_with_args(const char *src,
extern SPIPlanPtr SPI_prepare(const char *src, int nargs, Oid *argtypes);
extern SPIPlanPtr SPI_prepare_cursor(const char *src, int nargs, Oid *argtypes,
int cursorOptions);
extern SPIPlanPtr SPI_prepare_params(const char *src,
ParserSetupHook parserSetup,
void *parserSetupArg,
int cursorOptions);
extern SPIPlanPtr SPI_saveplan(SPIPlanPtr plan);
extern int SPI_freeplan(SPIPlanPtr plan);
......@@ -122,6 +129,8 @@ extern Portal SPI_cursor_open_with_args(const char *name,
int nargs, Oid *argtypes,
Datum *Values, const char *Nulls,
bool read_only, int cursorOptions);
extern Portal SPI_cursor_open_with_paramlist(const char *name, SPIPlanPtr plan,
ParamListInfo params, bool read_only);
extern Portal SPI_cursor_find(const char *name);
extern void SPI_cursor_fetch(Portal portal, bool forward, long count);
extern void SPI_cursor_move(Portal portal, bool forward, long count);
......
......@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/executor/spi_priv.h,v 1.32 2009/01/01 17:23:59 momjian Exp $
* $PostgreSQL: pgsql/src/include/executor/spi_priv.h,v 1.33 2009/11/04 22:26:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -68,6 +68,8 @@ typedef struct _SPI_plan
int cursor_options; /* Cursor options used for planning */
int nargs; /* number of plan arguments */
Oid *argtypes; /* Argument types (NULL if nargs is 0) */
ParserSetupHook parserSetup; /* alternative parameter spec method */
void *parserSetupArg;
} _SPI_plan;
#endif /* SPI_PRIV_H */
......@@ -7,13 +7,16 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/params.h,v 1.38 2009/01/01 17:24:00 momjian Exp $
* $PostgreSQL: pgsql/src/include/nodes/params.h,v 1.39 2009/11/04 22:26:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef PARAMS_H
#define PARAMS_H
/* To avoid including a pile of parser headers, reference ParseState thus: */
struct ParseState;
/* ----------------
* ParamListInfo
......@@ -26,10 +29,20 @@
* Although parameter numbers are normally consecutive, we allow
* ptype == InvalidOid to signal an unused array entry.
*
* pflags is a flags field. Currently the only used bit is:
* 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).
*
* 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.
* ----------------
......@@ -45,14 +58,22 @@ typedef struct ParamExternData
Oid ptype; /* parameter's datatype, or 0 */
} ParamExternData;
typedef struct ParamListInfoData *ParamListInfo;
typedef void (*ParamFetchHook) (ParamListInfo params, int paramid);
typedef void (*ParserSetupHook) (struct ParseState *pstate, void *arg);
typedef struct ParamListInfoData
{
ParamFetchHook paramFetch; /* parameter fetch hook */
void *paramFetchArg;
ParserSetupHook parserSetup; /* parser setup hook */
void *parserSetupArg;
int numParams; /* number of ParamExternDatas following */
ParamExternData params[1]; /* VARIABLE LENGTH ARRAY */
} ParamListInfoData;
typedef ParamListInfoData *ParamListInfo;
/* ----------------
* ParamExecData
......@@ -82,7 +103,7 @@ typedef struct ParamExecData
/* Functions found in src/backend/nodes/params.c */
extern ParamListInfo copyParamList(ParamListInfo from);
extern void getParamListTypes(ParamListInfo params,
Oid **param_types, int *num_params);
extern void setupParserWithParamList(struct ParseState *pstate,
ParamListInfo params);
#endif /* PARAMS_H */
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.100 2009/09/01 00:09:42 tgl Exp $
* $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.101 2009/11/04 22:26:07 tgl Exp $
*
* OLD COMMENTS
* This file was created so that other c files could get the two
......@@ -49,6 +49,10 @@ extern List *pg_parse_and_rewrite(const char *query_string,
extern List *pg_parse_query(const char *query_string);
extern List *pg_analyze_and_rewrite(Node *parsetree, const char *query_string,
Oid *paramTypes, int numParams);
extern List *pg_analyze_and_rewrite_params(Node *parsetree,
const char *query_string,
ParserSetupHook parserSetup,
void *parserSetupArg);
extern PlannedStmt *pg_plan_query(Query *querytree, int cursorOptions,
ParamListInfo boundParams);
extern List *pg_plan_queries(List *querytrees, int cursorOptions,
......
......@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/utils/plancache.h,v 1.15 2009/01/01 17:24:02 momjian Exp $
* $PostgreSQL: pgsql/src/include/utils/plancache.h,v 1.16 2009/11/04 22:26:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -16,6 +16,7 @@
#define PLANCACHE_H
#include "access/tupdesc.h"
#include "nodes/params.h"
/*
* CachedPlanSource represents the portion of a cached plan that persists
......@@ -50,6 +51,8 @@ typedef struct CachedPlanSource
const char *commandTag; /* command tag (a constant!), or NULL */
Oid *param_types; /* array of parameter type OIDs, or NULL */
int num_params; /* length of param_types array */
ParserSetupHook parserSetup; /* alternative parameter spec method */
void *parserSetupArg;
int cursor_options; /* cursor options used for planning */
bool fully_planned; /* do we cache planner or rewriter output? */
bool fixed_result; /* disallow change in result tupdesc? */
......@@ -105,6 +108,9 @@ extern CachedPlanSource *FastCreateCachedPlan(Node *raw_parse_tree,
bool fully_planned,
bool fixed_result,
MemoryContext context);
extern void CachedPlanSetParserHook(CachedPlanSource *plansource,
ParserSetupHook parserSetup,
void *parserSetupArg);
extern void DropCachedPlan(CachedPlanSource *plansource);
extern CachedPlan *RevalidateCachedPlan(CachedPlanSource *plansource,
bool useResOwner);
......
......@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.128 2009/09/29 20:05:29 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.129 2009/11/04 22:26:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -109,7 +109,7 @@ static List *read_raise_options(void);
} loop_body;
List *list;
PLpgSQL_type *dtype;
PLpgSQL_datum *scalar; /* a VAR, RECFIELD, or TRIGARG */
PLpgSQL_datum *scalar; /* a VAR or RECFIELD */
PLpgSQL_variable *variable; /* a VAR, REC, or ROW */
PLpgSQL_var *var;
PLpgSQL_row *row;
......@@ -236,7 +236,7 @@ static List *read_raise_options(void);
*/
%token T_STRING
%token T_NUMBER
%token T_SCALAR /* a VAR, RECFIELD, or TRIGARG */
%token T_SCALAR /* a VAR or RECFIELD */
%token T_ROW
%token T_RECORD
%token T_DTYPE
......@@ -1903,44 +1903,6 @@ lno :
%%
#define MAX_EXPR_PARAMS 1024
/*
* determine the expression parameter position to use for a plpgsql datum
*
* It is important that any given plpgsql datum map to just one parameter.
* We used to be sloppy and assign a separate parameter for each occurrence
* of a datum reference, but that fails for situations such as "select DATUM
* from ... group by DATUM".
*
* The params[] array must be of size MAX_EXPR_PARAMS.
*/
static int
assign_expr_param(int dno, int *params, int *nparams)
{
int i;
/* already have an instance of this dno? */
for (i = 0; i < *nparams; i++)
{
if (params[i] == dno)
return i+1;
}
/* check for array overflow */
if (*nparams >= MAX_EXPR_PARAMS)
{
plpgsql_error_lineno = plpgsql_scanner_lineno();
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many variables specified in SQL statement")));
}
/* add new parameter dno to array */
params[*nparams] = dno;
(*nparams)++;
return *nparams;
}
/* Convenience routine to read an expression with one possible terminator */
PLpgSQL_expr *
plpgsql_read_expression(int until, const char *expected)
......@@ -1993,8 +1955,7 @@ read_sql_construct(int until,
int lno;
StringInfoData ds;
int parenlevel = 0;
int nparams = 0;
int params[MAX_EXPR_PARAMS];
Bitmapset *paramnos = NULL;
char buf[32];
PLpgSQL_expr *expr;
......@@ -2047,24 +2008,21 @@ read_sql_construct(int until,
switch (tok)
{
case T_SCALAR:
snprintf(buf, sizeof(buf), " $%d ",
assign_expr_param(yylval.scalar->dno,
params, &nparams));
snprintf(buf, sizeof(buf), " $%d ", yylval.scalar->dno + 1);
appendStringInfoString(&ds, buf);
paramnos = bms_add_member(paramnos, yylval.scalar->dno);
break;
case T_ROW:
snprintf(buf, sizeof(buf), " $%d ",
assign_expr_param(yylval.row->dno,
params, &nparams));
snprintf(buf, sizeof(buf), " $%d ", yylval.row->dno + 1);
appendStringInfoString(&ds, buf);
paramnos = bms_add_member(paramnos, yylval.row->dno);
break;
case T_RECORD:
snprintf(buf, sizeof(buf), " $%d ",
assign_expr_param(yylval.rec->dno,
params, &nparams));
snprintf(buf, sizeof(buf), " $%d ", yylval.rec->dno + 1);
appendStringInfoString(&ds, buf);
paramnos = bms_add_member(paramnos, yylval.rec->dno);
break;
default:
......@@ -2076,13 +2034,11 @@ read_sql_construct(int until,
if (endtoken)
*endtoken = tok;
expr = palloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
expr = palloc0(sizeof(PLpgSQL_expr));
expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = pstrdup(ds.data);
expr->plan = NULL;
expr->nparams = nparams;
while(nparams-- > 0)
expr->params[nparams] = params[nparams];
expr->paramnos = paramnos;
pfree(ds.data);
if (valid_sql)
......@@ -2162,8 +2118,7 @@ static PLpgSQL_stmt *
make_execsql_stmt(const char *sqlstart, int lineno)
{
StringInfoData ds;
int nparams = 0;
int params[MAX_EXPR_PARAMS];
Bitmapset *paramnos = NULL;
char buf[32];
PLpgSQL_stmt_execsql *execsql;
PLpgSQL_expr *expr;
......@@ -2214,24 +2169,21 @@ make_execsql_stmt(const char *sqlstart, int lineno)
switch (tok)
{
case T_SCALAR:
snprintf(buf, sizeof(buf), " $%d ",
assign_expr_param(yylval.scalar->dno,
params, &nparams));
snprintf(buf, sizeof(buf), " $%d ", yylval.scalar->dno + 1);
appendStringInfoString(&ds, buf);
paramnos = bms_add_member(paramnos, yylval.scalar->dno);
break;
case T_ROW:
snprintf(buf, sizeof(buf), " $%d ",
assign_expr_param(yylval.row->dno,
params, &nparams));
snprintf(buf, sizeof(buf), " $%d ", yylval.row->dno + 1);
appendStringInfoString(&ds, buf);
paramnos = bms_add_member(paramnos, yylval.row->dno);
break;
case T_RECORD:
snprintf(buf, sizeof(buf), " $%d ",
assign_expr_param(yylval.rec->dno,
params, &nparams));
snprintf(buf, sizeof(buf), " $%d ", yylval.rec->dno + 1);
appendStringInfoString(&ds, buf);
paramnos = bms_add_member(paramnos, yylval.rec->dno);
break;
default:
......@@ -2240,13 +2192,11 @@ make_execsql_stmt(const char *sqlstart, int lineno)
}
}
expr = palloc(sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
expr = palloc0(sizeof(PLpgSQL_expr));
expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = pstrdup(ds.data);
expr->plan = NULL;
expr->nparams = nparams;
while(nparams-- > 0)
expr->params[nparams] = params[nparams];
expr->paramnos = paramnos;
pfree(ds.data);
check_sql_expr(expr->query);
......@@ -2600,9 +2550,6 @@ check_assignable(PLpgSQL_datum *datum)
case PLPGSQL_DTYPE_ARRAYELEM:
/* always assignable? */
break;
case PLPGSQL_DTYPE_TRIGARG:
yyerror("cannot assign to tg_argv");
break;
default:
elog(ERROR, "unrecognized dtype: %d", datum->dtype);
break;
......@@ -3095,24 +3042,10 @@ make_case(int lineno, PLpgSQL_expr *t_expr,
{
PLpgSQL_case_when *cwt = (PLpgSQL_case_when *) lfirst(l);
PLpgSQL_expr *expr = cwt->expr;
int nparams = expr->nparams;
PLpgSQL_expr *new_expr;
StringInfoData ds;
/* Must add the CASE variable as an extra param to expression */
if (nparams >= MAX_EXPR_PARAMS)
{
plpgsql_error_lineno = cwt->lineno;
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("too many variables specified in SQL statement")));
}
new_expr = palloc(sizeof(PLpgSQL_expr) + sizeof(int) * (nparams + 1) - sizeof(int));
memcpy(new_expr, expr,
sizeof(PLpgSQL_expr) + sizeof(int) * nparams - sizeof(int));
new_expr->nparams = nparams + 1;
new_expr->params[nparams] = t_varno;
expr->paramnos = bms_add_member(expr->paramnos, t_varno);
/* copy expression query without SELECT keyword (expr->query + 7) */
Assert(strncmp(expr->query, "SELECT ", 7) == 0);
......@@ -3120,17 +3053,14 @@ make_case(int lineno, PLpgSQL_expr *t_expr,
/* And do the string hacking */
initStringInfo(&ds);
appendStringInfo(&ds, "SELECT $%d IN(%s)",
nparams + 1,
expr->query + 7);
appendStringInfo(&ds, "SELECT $%d IN (%s)",
t_varno + 1,
expr->query + 7);
new_expr->query = pstrdup(ds.data);
pfree(ds.data);
pfree(expr->query);
pfree(expr);
expr->query = pstrdup(ds.data);
cwt->expr = new_expr;
pfree(ds.data);
}
}
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.139 2009/09/22 23:43:42 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.140 2009/11/04 22:26:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -624,20 +624,24 @@ do_compile(FunctionCallInfo fcinfo,
true);
function->tg_table_name_varno = var->dno;
/* add variable tg_table_schema */
/* add the variable tg_table_schema */
var = plpgsql_build_variable("tg_table_schema", 0,
plpgsql_build_datatype(NAMEOID, -1),
true);
function->tg_table_schema_varno = var->dno;
/* Add the variable tg_nargs */
var = plpgsql_build_variable("tg_nargs", 0,
plpgsql_build_datatype(INT4OID, -1),
true);
function->tg_nargs_varno = var->dno;
/* Add the variable tg_argv */
var = plpgsql_build_variable("tg_argv", 0,
plpgsql_build_datatype(TEXTARRAYOID, -1),
true);
function->tg_argv_varno = var->dno;
break;
default:
......@@ -931,34 +935,6 @@ plpgsql_parse_word(const char *word)
/* Do case conversion and word separation */
plpgsql_convert_ident(word, cp, 1);
/*
* Recognize tg_argv when compiling triggers (XXX this sucks, it should be
* a regular variable in the namestack)
*/
if (plpgsql_curr_compile->fn_is_trigger)
{
if (strcmp(cp[0], "tg_argv") == 0)
{
bool save_spacescanned = plpgsql_SpaceScanned;
PLpgSQL_trigarg *trigarg;
trigarg = palloc0(sizeof(PLpgSQL_trigarg));
trigarg->dtype = PLPGSQL_DTYPE_TRIGARG;
if (plpgsql_yylex() != '[')
plpgsql_yyerror("expected \"[\"");
trigarg->argnum = plpgsql_read_expression(']', "]");
plpgsql_adddatum((PLpgSQL_datum *) trigarg);
plpgsql_yylval.scalar = (PLpgSQL_datum *) trigarg;
plpgsql_SpaceScanned = save_spacescanned;
pfree(cp[0]);
return T_SCALAR;
}
}
/*
* Do a lookup on the compiler's namestack
*/
......
This diff is collapsed.
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.81 2009/09/29 20:05:29 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.82 2009/11/04 22:26:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1148,21 +1148,7 @@ dump_getdiag(PLpgSQL_stmt_getdiag *stmt)
static void
dump_expr(PLpgSQL_expr *expr)
{
int i;
printf("'%s", expr->query);
if (expr->nparams > 0)
{
printf(" {");
for (i = 0; i < expr->nparams; i++)
{
if (i > 0)
printf(", ");
printf("$%d=%d", i + 1, expr->params[i]);
}
printf("}");
}
printf("'");
printf("'%s'", expr->query);
}
void
......@@ -1240,11 +1226,6 @@ plpgsql_dumptree(PLpgSQL_function *func)
dump_expr(((PLpgSQL_arrayelem *) d)->subscript);
printf("\n");
break;
case PLPGSQL_DTYPE_TRIGARG:
printf("TRIGARG ");
dump_expr(((PLpgSQL_trigarg *) d)->argnum);
printf("\n");
break;
default:
printf("??? unknown data type %d\n", d->dtype);
}
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.46 2009/09/22 23:43:42 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.47 2009/11/04 22:26:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -68,6 +68,7 @@ Datum
plpgsql_call_handler(PG_FUNCTION_ARGS)
{
PLpgSQL_function *func;
PLpgSQL_execstate *save_cur_estate;
Datum retval;
int rc;
......@@ -80,6 +81,9 @@ plpgsql_call_handler(PG_FUNCTION_ARGS)
/* Find or compile the function */
func = plpgsql_compile(fcinfo, false);
/* Must save and restore prior value of cur_estate */
save_cur_estate = func->cur_estate;
/* Mark the function as busy, so it can't be deleted from under us */
func->use_count++;
......@@ -97,14 +101,17 @@ plpgsql_call_handler(PG_FUNCTION_ARGS)
}
PG_CATCH();
{
/* Decrement use-count and propagate error */
/* Decrement use-count, restore cur_estate, and propagate error */
func->use_count--;
func->cur_estate = save_cur_estate;
PG_RE_THROW();
}
PG_END_TRY();
func->use_count--;
func->cur_estate = save_cur_estate;
/*
* Disconnect from SPI manager
*/
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.117 2009/09/29 20:05:29 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.118 2009/11/04 22:26:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -22,6 +22,7 @@
#include "fmgr.h"
#include "commands/trigger.h"
#include "executor/spi.h"
#include "nodes/bitmapset.h"
#include "utils/tuplestore.h"
/**********************************************************************
......@@ -58,8 +59,7 @@ enum
PLPGSQL_DTYPE_REC,
PLPGSQL_DTYPE_RECFIELD,
PLPGSQL_DTYPE_ARRAYELEM,
PLPGSQL_DTYPE_EXPR,
PLPGSQL_DTYPE_TRIGARG
PLPGSQL_DTYPE_EXPR
};
/* ----------
......@@ -162,8 +162,7 @@ typedef struct
/*
* PLpgSQL_datum is the common supertype for PLpgSQL_expr, PLpgSQL_var,
* PLpgSQL_row, PLpgSQL_rec, PLpgSQL_recfield, PLpgSQL_arrayelem, and
* PLpgSQL_trigarg
* PLpgSQL_row, PLpgSQL_rec, PLpgSQL_recfield, and PLpgSQL_arrayelem
*/
typedef struct
{ /* Generic datum array item */
......@@ -189,7 +188,11 @@ typedef struct PLpgSQL_expr
int dno;
char *query;
SPIPlanPtr plan;
Oid *plan_argtypes;
Bitmapset *paramnos; /* all dnos referenced by this query */
/* function containing this expr (not set until we first parse query) */
struct PLpgSQL_function *func;
/* fields for "simple expression" fast-path execution: */
Expr *expr_simple_expr; /* NULL means not a simple expr */
int expr_simple_generation; /* plancache generation we checked */
......@@ -202,10 +205,6 @@ typedef struct PLpgSQL_expr
*/
ExprState *expr_simple_state;
LocalTransactionId expr_simple_lxid;
/* params to pass to expr */
int nparams;
int params[1]; /* VARIABLE SIZE ARRAY ... must be last */
} PLpgSQL_expr;
......@@ -284,14 +283,6 @@ typedef struct
} PLpgSQL_arrayelem;
typedef struct
{ /* Positional argument to trigger */
int dtype;
int dno;
PLpgSQL_expr *argnum;
} PLpgSQL_trigarg;
typedef struct
{ /* Item in the compilers namestack */
int itemtype;
......@@ -670,17 +661,22 @@ typedef struct PLpgSQL_function
int tg_table_name_varno;
int tg_table_schema_varno;
int tg_nargs_varno;
int tg_argv_varno;
int ndatums;
PLpgSQL_datum **datums;
PLpgSQL_stmt_block *action;
/* these fields change when the function is used */
struct PLpgSQL_execstate *cur_estate;
unsigned long use_count;
} PLpgSQL_function;
typedef struct
typedef struct PLpgSQL_execstate
{ /* Runtime execution data */
PLpgSQL_function *func; /* function being executed */
Datum retval;
bool retisnull;
Oid rettype; /* type of current retval */
......@@ -699,9 +695,6 @@ typedef struct
MemoryContext tuple_store_cxt;
ReturnSetInfo *rsi;
int trig_nargs;
Datum *trig_argv;
int found_varno;
int ndatums;
PLpgSQL_datum **datums;
......@@ -711,11 +704,12 @@ typedef struct
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_function *err_func; /* current func */
PLpgSQL_stmt *err_stmt; /* current stmt */
const char *err_text; /* additional state info */
void *plugin_info; /* reserved for use by optional plugin */
} PLpgSQL_execstate;
......
......@@ -285,11 +285,9 @@ begin
if new.slotno < 1 or new.slotno > hubrec.nslots then
raise exception ''no manual manipulation of HSlot'';
end if;
if tg_op = ''UPDATE'' then
if new.hubname != old.hubname then
if count(*) > 0 from Hub where name = old.hubname then
raise exception ''no manual manipulation of HSlot'';
end if;
if tg_op = ''UPDATE'' and new.hubname != old.hubname then
if count(*) > 0 from Hub where name = old.hubname then
raise exception ''no manual manipulation of HSlot'';
end if;
end if;
sname := ''HS.'' || trim(new.hubname);
......
......@@ -347,11 +347,9 @@ begin
if new.slotno < 1 or new.slotno > hubrec.nslots then
raise exception ''no manual manipulation of HSlot'';
end if;
if tg_op = ''UPDATE'' then
if new.hubname != old.hubname then
if count(*) > 0 from Hub where name = old.hubname then
raise exception ''no manual manipulation of HSlot'';
end if;
if tg_op = ''UPDATE'' and new.hubname != old.hubname then
if count(*) > 0 from Hub where name = old.hubname then
raise exception ''no manual manipulation of HSlot'';
end if;
end if;
sname := ''HS.'' || trim(new.hubname);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment