mirror of
https://github.com/postgres/postgres.git
synced 2025-12-01 12:18:01 +03:00
First phase of plan-invalidation project: create a plan cache management
module and teach PREPARE and protocol-level prepared statements to use it. In service of this, rearrange utility-statement processing so that parse analysis does not assume table schemas can't change before execution for utility statements (necessary because we don't attempt to re-acquire locks for utility statements when reusing a stored plan). This requires some refactoring of the ProcessUtility API, but it ends up cleaner anyway, for instance we can get rid of the QueryContext global. Still to do: fix up SPI and related code to use the plan cache; I'm tempted to try to make SQL functions use it too. Also, there are at least some aspects of system state that we want to ensure remain the same during a replan as in the original processing; search_path certainly ought to behave that way for instance, and perhaps there are others.
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.527 2007/03/03 19:32:54 neilc Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.528 2007/03/13 00:33:42 tgl Exp $
|
||||
*
|
||||
* NOTES
|
||||
* this is the "main" module of the postgres backend and
|
||||
@@ -140,8 +140,9 @@ static bool ignore_till_sync = false;
|
||||
* We keep it separate from the hashtable kept by commands/prepare.c
|
||||
* in order to reduce overhead for short-lived queries.
|
||||
*/
|
||||
static CachedPlanSource *unnamed_stmt_psrc = NULL;
|
||||
/* workspace for building a new unnamed statement in */
|
||||
static MemoryContext unnamed_stmt_context = NULL;
|
||||
static PreparedStatement *unnamed_stmt_pstmt = NULL;
|
||||
|
||||
|
||||
static bool EchoQuery = false; /* default don't echo */
|
||||
@@ -173,6 +174,7 @@ static void finish_xact_command(void);
|
||||
static bool IsTransactionExitStmt(Node *parsetree);
|
||||
static bool IsTransactionExitStmtList(List *parseTrees);
|
||||
static bool IsTransactionStmtList(List *parseTrees);
|
||||
static void drop_unnamed_stmt(void);
|
||||
static void SigHupHandler(SIGNAL_ARGS);
|
||||
static void log_disconnections(int code, Datum arg);
|
||||
|
||||
@@ -794,21 +796,13 @@ exec_simple_query(const char *query_string)
|
||||
* statement and portal; this ensures we recover any storage used by prior
|
||||
* unnamed operations.)
|
||||
*/
|
||||
unnamed_stmt_pstmt = NULL;
|
||||
if (unnamed_stmt_context)
|
||||
{
|
||||
DropDependentPortals(unnamed_stmt_context);
|
||||
MemoryContextDelete(unnamed_stmt_context);
|
||||
}
|
||||
unnamed_stmt_context = NULL;
|
||||
drop_unnamed_stmt();
|
||||
|
||||
/*
|
||||
* Switch to appropriate context for constructing parsetrees.
|
||||
*/
|
||||
oldcontext = MemoryContextSwitchTo(MessageContext);
|
||||
|
||||
QueryContext = CurrentMemoryContext;
|
||||
|
||||
/*
|
||||
* Do basic parsing of the query or queries (this should be safe even if
|
||||
* we are in aborted transaction state!)
|
||||
@@ -906,7 +900,7 @@ exec_simple_query(const char *query_string)
|
||||
query_string,
|
||||
commandTag,
|
||||
plantree_list,
|
||||
MessageContext);
|
||||
NULL);
|
||||
|
||||
/*
|
||||
* Start the portal. No parameters here.
|
||||
@@ -950,6 +944,7 @@ exec_simple_query(const char *query_string)
|
||||
*/
|
||||
(void) PortalRun(portal,
|
||||
FETCH_ALL,
|
||||
true, /* top level */
|
||||
receiver,
|
||||
receiver,
|
||||
completionTag);
|
||||
@@ -1009,8 +1004,6 @@ exec_simple_query(const char *query_string)
|
||||
if (!parsetree_list)
|
||||
NullCommand(dest);
|
||||
|
||||
QueryContext = NULL;
|
||||
|
||||
/*
|
||||
* Emit duration logging if appropriate.
|
||||
*/
|
||||
@@ -1049,10 +1042,10 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||
{
|
||||
MemoryContext oldcontext;
|
||||
List *parsetree_list;
|
||||
Node *raw_parse_tree;
|
||||
const char *commandTag;
|
||||
List *querytree_list,
|
||||
*stmt_list,
|
||||
*param_list;
|
||||
*stmt_list;
|
||||
bool is_named;
|
||||
bool fully_planned;
|
||||
bool save_log_statement_stats = log_statement_stats;
|
||||
@@ -1088,12 +1081,12 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||
* We have two strategies depending on whether the prepared statement is
|
||||
* named or not. For a named prepared statement, we do parsing in
|
||||
* MessageContext and copy the finished trees into the prepared
|
||||
* statement's private context; then the reset of MessageContext releases
|
||||
* statement's plancache entry; then the reset of MessageContext releases
|
||||
* temporary space used by parsing and planning. For an unnamed prepared
|
||||
* 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.
|
||||
* copying parse/plan trees. So in this case, we create the plancache
|
||||
* entry's context here, and do all the parsing work therein.
|
||||
*/
|
||||
is_named = (stmt_name[0] != '\0');
|
||||
if (is_named)
|
||||
@@ -1104,16 +1097,10 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||
else
|
||||
{
|
||||
/* Unnamed prepared statement --- release any prior unnamed stmt */
|
||||
unnamed_stmt_pstmt = NULL;
|
||||
if (unnamed_stmt_context)
|
||||
{
|
||||
DropDependentPortals(unnamed_stmt_context);
|
||||
MemoryContextDelete(unnamed_stmt_context);
|
||||
}
|
||||
unnamed_stmt_context = NULL;
|
||||
/* create context for parsing/planning */
|
||||
drop_unnamed_stmt();
|
||||
/* Create context for parsing/planning */
|
||||
unnamed_stmt_context =
|
||||
AllocSetContextCreate(TopMemoryContext,
|
||||
AllocSetContextCreate(CacheMemoryContext,
|
||||
"unnamed prepared statement",
|
||||
ALLOCSET_DEFAULT_MINSIZE,
|
||||
ALLOCSET_DEFAULT_INITSIZE,
|
||||
@@ -1121,8 +1108,6 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||
oldcontext = MemoryContextSwitchTo(unnamed_stmt_context);
|
||||
}
|
||||
|
||||
QueryContext = CurrentMemoryContext;
|
||||
|
||||
/*
|
||||
* Do basic parsing of the query or queries (this should be safe even if
|
||||
* we are in aborted transaction state!)
|
||||
@@ -1141,13 +1126,14 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||
|
||||
if (parsetree_list != NIL)
|
||||
{
|
||||
Node *parsetree = (Node *) linitial(parsetree_list);
|
||||
int i;
|
||||
|
||||
raw_parse_tree = (Node *) linitial(parsetree_list);
|
||||
|
||||
/*
|
||||
* Get the command name for possible use in status display.
|
||||
*/
|
||||
commandTag = CreateCommandTag(parsetree);
|
||||
commandTag = CreateCommandTag(raw_parse_tree);
|
||||
|
||||
/*
|
||||
* If we are in an aborted transaction, reject all commands except
|
||||
@@ -1158,7 +1144,7 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||
* state, but not many...)
|
||||
*/
|
||||
if (IsAbortedTransactionBlockState() &&
|
||||
!IsTransactionExitStmt(parsetree))
|
||||
!IsTransactionExitStmt(raw_parse_tree))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
|
||||
errmsg("current transaction is aborted, "
|
||||
@@ -1168,20 +1154,22 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||
* OK to analyze, rewrite, and plan this query. Note that the
|
||||
* originally specified parameter set is not required to be complete,
|
||||
* so we have to use parse_analyze_varparams().
|
||||
*
|
||||
* XXX must use copyObject here since parse analysis scribbles on
|
||||
* its input, and we need the unmodified raw parse tree for possible
|
||||
* replanning later.
|
||||
*/
|
||||
if (log_parser_stats)
|
||||
ResetUsage();
|
||||
|
||||
querytree_list = parse_analyze_varparams(parsetree,
|
||||
querytree_list = parse_analyze_varparams(copyObject(raw_parse_tree),
|
||||
query_string,
|
||||
¶mTypes,
|
||||
&numParams);
|
||||
|
||||
/*
|
||||
* Check all parameter types got determined, and convert array
|
||||
* representation to a list for storage.
|
||||
* Check all parameter types got determined.
|
||||
*/
|
||||
param_list = NIL;
|
||||
for (i = 0; i < numParams; i++)
|
||||
{
|
||||
Oid ptype = paramTypes[i];
|
||||
@@ -1191,7 +1179,6 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||
(errcode(ERRCODE_INDETERMINATE_DATATYPE),
|
||||
errmsg("could not determine data type of parameter $%d",
|
||||
i + 1)));
|
||||
param_list = lappend_oid(param_list, ptype);
|
||||
}
|
||||
|
||||
if (log_parser_stats)
|
||||
@@ -1217,9 +1204,9 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||
else
|
||||
{
|
||||
/* Empty input string. This is legal. */
|
||||
raw_parse_tree = NULL;
|
||||
commandTag = NULL;
|
||||
stmt_list = NIL;
|
||||
param_list = NIL;
|
||||
fully_planned = true;
|
||||
}
|
||||
|
||||
@@ -1232,35 +1219,33 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||
if (is_named)
|
||||
{
|
||||
StorePreparedStatement(stmt_name,
|
||||
raw_parse_tree,
|
||||
query_string,
|
||||
commandTag,
|
||||
paramTypes,
|
||||
numParams,
|
||||
stmt_list,
|
||||
param_list,
|
||||
fully_planned,
|
||||
false);
|
||||
}
|
||||
else
|
||||
{
|
||||
PreparedStatement *pstmt;
|
||||
|
||||
pstmt = (PreparedStatement *) palloc0(sizeof(PreparedStatement));
|
||||
/* query_string needs to be copied into unnamed_stmt_context */
|
||||
pstmt->query_string = pstrdup(query_string);
|
||||
/* the rest is there already */
|
||||
pstmt->commandTag = commandTag;
|
||||
pstmt->stmt_list = stmt_list;
|
||||
pstmt->argtype_list = param_list;
|
||||
pstmt->fully_planned = fully_planned;
|
||||
pstmt->from_sql = false;
|
||||
pstmt->context = unnamed_stmt_context;
|
||||
/* Now the unnamed statement is complete and valid */
|
||||
unnamed_stmt_pstmt = pstmt;
|
||||
unnamed_stmt_psrc = FastCreateCachedPlan(raw_parse_tree,
|
||||
pstrdup(query_string),
|
||||
commandTag,
|
||||
paramTypes,
|
||||
numParams,
|
||||
stmt_list,
|
||||
fully_planned,
|
||||
true,
|
||||
unnamed_stmt_context);
|
||||
/* context now belongs to the plancache entry */
|
||||
unnamed_stmt_context = NULL;
|
||||
}
|
||||
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
|
||||
QueryContext = NULL;
|
||||
|
||||
/*
|
||||
* We do NOT close the open transaction command here; that only happens
|
||||
* when the client sends Sync. Instead, do CommandCounterIncrement just
|
||||
@@ -1315,12 +1300,11 @@ exec_bind_message(StringInfo input_message)
|
||||
int numParams;
|
||||
int numRFormats;
|
||||
int16 *rformats = NULL;
|
||||
PreparedStatement *pstmt;
|
||||
CachedPlanSource *psrc;
|
||||
CachedPlan *cplan;
|
||||
Portal portal;
|
||||
ParamListInfo params;
|
||||
List *query_list;
|
||||
List *plan_list;
|
||||
MemoryContext qContext;
|
||||
bool save_log_statement_stats = log_statement_stats;
|
||||
char msec_str[32];
|
||||
|
||||
@@ -1335,12 +1319,17 @@ exec_bind_message(StringInfo input_message)
|
||||
|
||||
/* Find prepared statement */
|
||||
if (stmt_name[0] != '\0')
|
||||
{
|
||||
PreparedStatement *pstmt;
|
||||
|
||||
pstmt = FetchPreparedStatement(stmt_name, true);
|
||||
psrc = pstmt->plansource;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* special-case the unnamed statement */
|
||||
pstmt = unnamed_stmt_pstmt;
|
||||
if (!pstmt)
|
||||
psrc = unnamed_stmt_psrc;
|
||||
if (!psrc)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_PSTATEMENT),
|
||||
errmsg("unnamed prepared statement does not exist")));
|
||||
@@ -1349,7 +1338,7 @@ exec_bind_message(StringInfo input_message)
|
||||
/*
|
||||
* Report query to various monitoring facilities.
|
||||
*/
|
||||
debug_query_string = pstmt->query_string ? pstmt->query_string : "<BIND>";
|
||||
debug_query_string = psrc->query_string ? psrc->query_string : "<BIND>";
|
||||
|
||||
pgstat_report_activity(debug_query_string);
|
||||
|
||||
@@ -1388,11 +1377,11 @@ exec_bind_message(StringInfo input_message)
|
||||
errmsg("bind message has %d parameter formats but %d parameters",
|
||||
numPFormats, numParams)));
|
||||
|
||||
if (numParams != list_length(pstmt->argtype_list))
|
||||
if (numParams != psrc->num_params)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_PROTOCOL_VIOLATION),
|
||||
errmsg("bind message supplies %d parameters, but prepared statement \"%s\" requires %d",
|
||||
numParams, stmt_name, list_length(pstmt->argtype_list))));
|
||||
numParams, stmt_name, psrc->num_params)));
|
||||
|
||||
/*
|
||||
* If we are in aborted transaction state, the only portals we can
|
||||
@@ -1403,7 +1392,7 @@ exec_bind_message(StringInfo input_message)
|
||||
* functions.
|
||||
*/
|
||||
if (IsAbortedTransactionBlockState() &&
|
||||
(!IsTransactionExitStmtList(pstmt->stmt_list) ||
|
||||
(!IsTransactionExitStmt(psrc->raw_parse_tree) ||
|
||||
numParams != 0))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
|
||||
@@ -1424,7 +1413,6 @@ exec_bind_message(StringInfo input_message)
|
||||
*/
|
||||
if (numParams > 0)
|
||||
{
|
||||
ListCell *l;
|
||||
MemoryContext oldContext;
|
||||
int paramno;
|
||||
|
||||
@@ -1435,10 +1423,9 @@ exec_bind_message(StringInfo input_message)
|
||||
(numParams - 1) *sizeof(ParamExternData));
|
||||
params->numParams = numParams;
|
||||
|
||||
paramno = 0;
|
||||
foreach(l, pstmt->argtype_list)
|
||||
for (paramno = 0; paramno < numParams; paramno++)
|
||||
{
|
||||
Oid ptype = lfirst_oid(l);
|
||||
Oid ptype = psrc->param_types[paramno];
|
||||
int32 plength;
|
||||
Datum pval;
|
||||
bool isNull;
|
||||
@@ -1554,8 +1541,6 @@ exec_bind_message(StringInfo input_message)
|
||||
*/
|
||||
params->params[paramno].pflags = PARAM_FLAG_CONST;
|
||||
params->params[paramno].ptype = ptype;
|
||||
|
||||
paramno++;
|
||||
}
|
||||
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
@@ -1576,46 +1561,62 @@ exec_bind_message(StringInfo input_message)
|
||||
|
||||
pq_getmsgend(input_message);
|
||||
|
||||
/*
|
||||
* 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; this also means that the
|
||||
* portal's queryContext becomes its own heap context rather than the
|
||||
* prepared statement's context. FIXME someday
|
||||
*/
|
||||
if (pstmt->fully_planned)
|
||||
if (psrc->fully_planned)
|
||||
{
|
||||
plan_list = pstmt->stmt_list;
|
||||
qContext = pstmt->context;
|
||||
/*
|
||||
* Revalidate the cached plan; this may result in replanning. Any
|
||||
* cruft will be generated in MessageContext. The plan refcount
|
||||
* will be assigned to the Portal, so it will be released at portal
|
||||
* destruction.
|
||||
*/
|
||||
cplan = RevalidateCachedPlan(psrc, false);
|
||||
plan_list = cplan->stmt_list;
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryContext oldContext;
|
||||
List *query_list;
|
||||
|
||||
qContext = PortalGetHeapMemory(portal);
|
||||
oldContext = MemoryContextSwitchTo(qContext);
|
||||
query_list = copyObject(pstmt->stmt_list);
|
||||
/*
|
||||
* Revalidate the cached plan; this may result in redoing parse
|
||||
* analysis and rewriting (but not planning). Any cruft will be
|
||||
* generated in MessageContext. The plan refcount is assigned to
|
||||
* CurrentResourceOwner.
|
||||
*/
|
||||
cplan = RevalidateCachedPlan(psrc, true);
|
||||
|
||||
/*
|
||||
* We didn't plan the query before, so 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. FIXME someday.
|
||||
*/
|
||||
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
||||
query_list = copyObject(cplan->stmt_list);
|
||||
plan_list = pg_plan_queries(query_list, params, true);
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
|
||||
/* We no longer need the cached plan refcount ... */
|
||||
ReleaseCachedPlan(cplan, true);
|
||||
/* ... and we don't want the portal to depend on it, either */
|
||||
cplan = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Define portal and start execution.
|
||||
*/
|
||||
PortalDefineQuery(portal,
|
||||
*pstmt->stmt_name ? pstmt->stmt_name : NULL,
|
||||
pstmt->query_string,
|
||||
pstmt->commandTag,
|
||||
stmt_name[0] ? stmt_name : NULL,
|
||||
psrc->query_string,
|
||||
psrc->commandTag,
|
||||
plan_list,
|
||||
qContext);
|
||||
cplan);
|
||||
|
||||
PortalStart(portal, params, InvalidSnapshot);
|
||||
|
||||
@@ -1647,7 +1648,7 @@ exec_bind_message(StringInfo input_message)
|
||||
*stmt_name ? stmt_name : "<unnamed>",
|
||||
*portal_name ? "/" : "",
|
||||
*portal_name ? portal_name : "",
|
||||
pstmt->query_string ? pstmt->query_string : "<source not stored>"),
|
||||
psrc->query_string ? psrc->query_string : "<source not stored>"),
|
||||
errhidestmt(true),
|
||||
errdetail_params(params)));
|
||||
break;
|
||||
@@ -1809,6 +1810,7 @@ exec_execute_message(const char *portal_name, long max_rows)
|
||||
|
||||
completed = PortalRun(portal,
|
||||
max_rows,
|
||||
true, /* top level */
|
||||
receiver,
|
||||
receiver,
|
||||
completionTag);
|
||||
@@ -1981,9 +1983,9 @@ errdetail_execute(List *raw_parsetree_list)
|
||||
PreparedStatement *pstmt;
|
||||
|
||||
pstmt = FetchPreparedStatement(stmt->name, false);
|
||||
if (pstmt && pstmt->query_string)
|
||||
if (pstmt && pstmt->plansource->query_string)
|
||||
{
|
||||
errdetail("prepare: %s", pstmt->query_string);
|
||||
errdetail("prepare: %s", pstmt->plansource->query_string);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -2064,10 +2066,9 @@ errdetail_params(ParamListInfo params)
|
||||
static void
|
||||
exec_describe_statement_message(const char *stmt_name)
|
||||
{
|
||||
PreparedStatement *pstmt;
|
||||
TupleDesc tupdesc;
|
||||
ListCell *l;
|
||||
CachedPlanSource *psrc;
|
||||
StringInfoData buf;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Start up a transaction command. (Note that this will normally change
|
||||
@@ -2080,28 +2081,37 @@ exec_describe_statement_message(const char *stmt_name)
|
||||
|
||||
/* Find prepared statement */
|
||||
if (stmt_name[0] != '\0')
|
||||
{
|
||||
PreparedStatement *pstmt;
|
||||
|
||||
pstmt = FetchPreparedStatement(stmt_name, true);
|
||||
psrc = pstmt->plansource;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* special-case the unnamed statement */
|
||||
pstmt = unnamed_stmt_pstmt;
|
||||
if (!pstmt)
|
||||
psrc = unnamed_stmt_psrc;
|
||||
if (!psrc)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_UNDEFINED_PSTATEMENT),
|
||||
errmsg("unnamed prepared statement does not exist")));
|
||||
}
|
||||
|
||||
/* Prepared statements shouldn't have changeable result descs */
|
||||
Assert(psrc->fixed_result);
|
||||
|
||||
/*
|
||||
* If we are in aborted transaction state, we can't safely create a result
|
||||
* tupledesc, because that needs catalog accesses. Hence, refuse to
|
||||
* Describe statements that return data. (We shouldn't just refuse all
|
||||
* Describes, since that might break the ability of some clients to issue
|
||||
* COMMIT or ROLLBACK commands, if they use code that blindly Describes
|
||||
* whatever it does.) We can Describe parameters without doing anything
|
||||
* dangerous, so we don't restrict that.
|
||||
* If we are in aborted transaction state, we can't run
|
||||
* SendRowDescriptionMessage(), because that needs catalog accesses.
|
||||
* (We can't do RevalidateCachedPlan, either, but that's a lesser problem.)
|
||||
* Hence, refuse to Describe statements that return data. (We shouldn't
|
||||
* just refuse all Describes, since that might break the ability of some
|
||||
* clients to issue COMMIT or ROLLBACK commands, if they use code that
|
||||
* blindly Describes whatever it does.) We can Describe parameters
|
||||
* without doing anything dangerous, so we don't restrict that.
|
||||
*/
|
||||
if (IsAbortedTransactionBlockState() &&
|
||||
PreparedStatementReturnsTuples(pstmt))
|
||||
psrc->resultDesc)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
|
||||
errmsg("current transaction is aborted, "
|
||||
@@ -2114,11 +2124,11 @@ exec_describe_statement_message(const char *stmt_name)
|
||||
* First describe the parameters...
|
||||
*/
|
||||
pq_beginmessage(&buf, 't'); /* parameter description message type */
|
||||
pq_sendint(&buf, list_length(pstmt->argtype_list), 2);
|
||||
pq_sendint(&buf, psrc->num_params, 2);
|
||||
|
||||
foreach(l, pstmt->argtype_list)
|
||||
for (i = 0; i < psrc->num_params; i++)
|
||||
{
|
||||
Oid ptype = lfirst_oid(l);
|
||||
Oid ptype = psrc->param_types[i];
|
||||
|
||||
pq_sendint(&buf, (int) ptype, 4);
|
||||
}
|
||||
@@ -2127,11 +2137,21 @@ exec_describe_statement_message(const char *stmt_name)
|
||||
/*
|
||||
* Next send RowDescription or NoData to describe the result...
|
||||
*/
|
||||
tupdesc = FetchPreparedStatementResultDesc(pstmt);
|
||||
if (tupdesc)
|
||||
SendRowDescriptionMessage(tupdesc,
|
||||
FetchPreparedStatementTargetList(pstmt),
|
||||
NULL);
|
||||
if (psrc->resultDesc)
|
||||
{
|
||||
CachedPlan *cplan;
|
||||
List *tlist;
|
||||
|
||||
/* Make sure the plan is up to date */
|
||||
cplan = RevalidateCachedPlan(psrc, true);
|
||||
|
||||
/* Get the primary statement and find out what it returns */
|
||||
tlist = FetchStatementTargetList(PortalListGetPrimaryStmt(cplan->stmt_list));
|
||||
|
||||
SendRowDescriptionMessage(psrc->resultDesc, tlist, NULL);
|
||||
|
||||
ReleaseCachedPlan(cplan, true);
|
||||
}
|
||||
else
|
||||
pq_putemptymessage('n'); /* NoData */
|
||||
|
||||
@@ -2308,6 +2328,24 @@ IsTransactionStmtList(List *parseTrees)
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Release any existing unnamed prepared statement */
|
||||
static void
|
||||
drop_unnamed_stmt(void)
|
||||
{
|
||||
/* Release any completed unnamed statement */
|
||||
if (unnamed_stmt_psrc)
|
||||
DropCachedPlan(unnamed_stmt_psrc);
|
||||
unnamed_stmt_psrc = NULL;
|
||||
/*
|
||||
* If we failed while trying to build a prior unnamed statement, we may
|
||||
* have a memory context that wasn't assigned to a completed plancache
|
||||
* entry. If so, drop it to avoid a permanent memory leak.
|
||||
*/
|
||||
if (unnamed_stmt_context)
|
||||
MemoryContextDelete(unnamed_stmt_context);
|
||||
unnamed_stmt_context = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* --------------------------------
|
||||
* signal handler routines used in PostgresMain()
|
||||
@@ -3313,7 +3351,6 @@ PostgresMain(int argc, char *argv[], const char *username)
|
||||
*/
|
||||
MemoryContextSwitchTo(TopMemoryContext);
|
||||
FlushErrorState();
|
||||
QueryContext = NULL;
|
||||
|
||||
/*
|
||||
* If we were handling an extended-query-protocol message, initiate
|
||||
@@ -3558,13 +3595,7 @@ PostgresMain(int argc, char *argv[], const char *username)
|
||||
else
|
||||
{
|
||||
/* special-case the unnamed statement */
|
||||
unnamed_stmt_pstmt = NULL;
|
||||
if (unnamed_stmt_context)
|
||||
{
|
||||
DropDependentPortals(unnamed_stmt_context);
|
||||
MemoryContextDelete(unnamed_stmt_context);
|
||||
}
|
||||
unnamed_stmt_context = NULL;
|
||||
drop_unnamed_stmt();
|
||||
}
|
||||
break;
|
||||
case 'P':
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.114 2007/02/20 17:32:16 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.115 2007/03/13 00:33:42 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -36,14 +36,14 @@ static void ProcessQuery(PlannedStmt *plan,
|
||||
ParamListInfo params,
|
||||
DestReceiver *dest,
|
||||
char *completionTag);
|
||||
static void FillPortalStore(Portal portal);
|
||||
static void FillPortalStore(Portal portal, bool isTopLevel);
|
||||
static uint32 RunFromStore(Portal portal, ScanDirection direction, long count,
|
||||
DestReceiver *dest);
|
||||
static long PortalRunSelect(Portal portal, bool forward, long count,
|
||||
DestReceiver *dest);
|
||||
static void PortalRunUtility(Portal portal, Node *utilityStmt,
|
||||
static void PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
|
||||
DestReceiver *dest, char *completionTag);
|
||||
static void PortalRunMulti(Portal portal,
|
||||
static void PortalRunMulti(Portal portal, bool isTopLevel,
|
||||
DestReceiver *dest, DestReceiver *altdest,
|
||||
char *completionTag);
|
||||
static long DoPortalRunFetch(Portal portal,
|
||||
@@ -148,8 +148,7 @@ ProcessQuery(PlannedStmt *plan,
|
||||
{
|
||||
QueryDesc *queryDesc;
|
||||
|
||||
ereport(DEBUG3,
|
||||
(errmsg_internal("ProcessQuery")));
|
||||
elog(DEBUG3, "ProcessQuery");
|
||||
|
||||
/*
|
||||
* Must always set snapshot for plannable queries. Note we assume that
|
||||
@@ -232,8 +231,7 @@ ProcessQuery(PlannedStmt *plan,
|
||||
* Select portal execution strategy given the intended statement list.
|
||||
*
|
||||
* The list elements can be Querys, PlannedStmts, or utility statements.
|
||||
* That's more general than portals need, but we use this for prepared
|
||||
* statements as well.
|
||||
* That's more general than portals need, but plancache.c uses this too.
|
||||
*
|
||||
* See the comments in portal.h.
|
||||
*/
|
||||
@@ -358,8 +356,7 @@ FetchPortalTargetList(Portal portal)
|
||||
* Returns NIL if the statement doesn't have a determinable targetlist.
|
||||
*
|
||||
* This can be applied to a Query, a PlannedStmt, or a utility statement.
|
||||
* That's more general than portals need, but we use this for prepared
|
||||
* statements as well.
|
||||
* That's more general than portals need, but plancache.c uses this too.
|
||||
*
|
||||
* Note: do not modify the result.
|
||||
*
|
||||
@@ -452,11 +449,10 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
|
||||
int eflags;
|
||||
|
||||
AssertArg(PortalIsValid(portal));
|
||||
AssertState(portal->queryContext != NULL); /* query defined? */
|
||||
AssertState(portal->status == PORTAL_NEW); /* else extra PortalStart */
|
||||
AssertState(portal->status == PORTAL_DEFINED);
|
||||
|
||||
/*
|
||||
* Set up global portal context pointers. (Should we set QueryContext?)
|
||||
* Set up global portal context pointers.
|
||||
*/
|
||||
saveActivePortal = ActivePortal;
|
||||
saveActiveSnapshot = ActiveSnapshot;
|
||||
@@ -683,6 +679,9 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
|
||||
* interpreted as "all rows". Note that count is ignored in multi-query
|
||||
* situations, where we always run the portal to completion.
|
||||
*
|
||||
* isTopLevel: true if query is being executed at backend "top level"
|
||||
* (that is, directly from a client command message)
|
||||
*
|
||||
* dest: where to send output of primary (canSetTag) query
|
||||
*
|
||||
* altdest: where to send output of non-primary queries
|
||||
@@ -695,7 +694,7 @@ PortalSetResultFormat(Portal portal, int nFormats, int16 *formats)
|
||||
* suspended due to exhaustion of the count parameter.
|
||||
*/
|
||||
bool
|
||||
PortalRun(Portal portal, long count,
|
||||
PortalRun(Portal portal, long count, bool isTopLevel,
|
||||
DestReceiver *dest, DestReceiver *altdest,
|
||||
char *completionTag)
|
||||
{
|
||||
@@ -706,7 +705,6 @@ PortalRun(Portal portal, long count,
|
||||
Snapshot saveActiveSnapshot;
|
||||
ResourceOwner saveResourceOwner;
|
||||
MemoryContext savePortalContext;
|
||||
MemoryContext saveQueryContext;
|
||||
MemoryContext saveMemoryContext;
|
||||
|
||||
AssertArg(PortalIsValid(portal));
|
||||
@@ -717,8 +715,7 @@ PortalRun(Portal portal, long count,
|
||||
|
||||
if (log_executor_stats && portal->strategy != PORTAL_MULTI_QUERY)
|
||||
{
|
||||
ereport(DEBUG3,
|
||||
(errmsg_internal("PortalRun")));
|
||||
elog(DEBUG3, "PortalRun");
|
||||
/* PORTAL_MULTI_QUERY logs its own stats per query */
|
||||
ResetUsage();
|
||||
}
|
||||
@@ -752,7 +749,6 @@ PortalRun(Portal portal, long count,
|
||||
saveActiveSnapshot = ActiveSnapshot;
|
||||
saveResourceOwner = CurrentResourceOwner;
|
||||
savePortalContext = PortalContext;
|
||||
saveQueryContext = QueryContext;
|
||||
saveMemoryContext = CurrentMemoryContext;
|
||||
PG_TRY();
|
||||
{
|
||||
@@ -760,7 +756,6 @@ PortalRun(Portal portal, long count,
|
||||
ActiveSnapshot = NULL; /* will be set later */
|
||||
CurrentResourceOwner = portal->resowner;
|
||||
PortalContext = PortalGetHeapMemory(portal);
|
||||
QueryContext = portal->queryContext;
|
||||
|
||||
MemoryContextSwitchTo(PortalContext);
|
||||
|
||||
@@ -790,7 +785,7 @@ PortalRun(Portal portal, long count,
|
||||
* results in the portal's tuplestore.
|
||||
*/
|
||||
if (!portal->holdStore)
|
||||
FillPortalStore(portal);
|
||||
FillPortalStore(portal, isTopLevel);
|
||||
|
||||
/*
|
||||
* Now fetch desired portion of results.
|
||||
@@ -811,7 +806,8 @@ PortalRun(Portal portal, long count,
|
||||
break;
|
||||
|
||||
case PORTAL_MULTI_QUERY:
|
||||
PortalRunMulti(portal, dest, altdest, completionTag);
|
||||
PortalRunMulti(portal, isTopLevel,
|
||||
dest, altdest, completionTag);
|
||||
|
||||
/* Prevent portal's commands from being re-executed */
|
||||
portal->status = PORTAL_DONE;
|
||||
@@ -844,7 +840,6 @@ PortalRun(Portal portal, long count,
|
||||
else
|
||||
CurrentResourceOwner = saveResourceOwner;
|
||||
PortalContext = savePortalContext;
|
||||
QueryContext = saveQueryContext;
|
||||
|
||||
PG_RE_THROW();
|
||||
}
|
||||
@@ -861,7 +856,6 @@ PortalRun(Portal portal, long count,
|
||||
else
|
||||
CurrentResourceOwner = saveResourceOwner;
|
||||
PortalContext = savePortalContext;
|
||||
QueryContext = saveQueryContext;
|
||||
|
||||
if (log_executor_stats && portal->strategy != PORTAL_MULTI_QUERY)
|
||||
ShowUsage("EXECUTOR STATISTICS");
|
||||
@@ -1025,7 +1019,7 @@ PortalRunSelect(Portal portal,
|
||||
* This is used for PORTAL_ONE_RETURNING and PORTAL_UTIL_SELECT cases only.
|
||||
*/
|
||||
static void
|
||||
FillPortalStore(Portal portal)
|
||||
FillPortalStore(Portal portal, bool isTopLevel)
|
||||
{
|
||||
DestReceiver *treceiver;
|
||||
char completionTag[COMPLETION_TAG_BUFSIZE];
|
||||
@@ -1044,12 +1038,13 @@ FillPortalStore(Portal portal)
|
||||
* MULTI_QUERY case, but send the primary query's output to the
|
||||
* tuplestore. Auxiliary query outputs are discarded.
|
||||
*/
|
||||
PortalRunMulti(portal, treceiver, None_Receiver, completionTag);
|
||||
PortalRunMulti(portal, isTopLevel,
|
||||
treceiver, None_Receiver, completionTag);
|
||||
break;
|
||||
|
||||
case PORTAL_UTIL_SELECT:
|
||||
PortalRunUtility(portal, (Node *) linitial(portal->stmts),
|
||||
treceiver, completionTag);
|
||||
isTopLevel, treceiver, completionTag);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -1137,11 +1132,10 @@ RunFromStore(Portal portal, ScanDirection direction, long count,
|
||||
* Execute a utility statement inside a portal.
|
||||
*/
|
||||
static void
|
||||
PortalRunUtility(Portal portal, Node *utilityStmt,
|
||||
PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel,
|
||||
DestReceiver *dest, char *completionTag)
|
||||
{
|
||||
ereport(DEBUG3,
|
||||
(errmsg_internal("ProcessUtility")));
|
||||
elog(DEBUG3, "ProcessUtility");
|
||||
|
||||
/*
|
||||
* Set snapshot if utility stmt needs one. Most reliable way to do this
|
||||
@@ -1173,7 +1167,12 @@ PortalRunUtility(Portal portal, Node *utilityStmt,
|
||||
else
|
||||
ActiveSnapshot = NULL;
|
||||
|
||||
ProcessUtility(utilityStmt, portal->portalParams, dest, completionTag);
|
||||
ProcessUtility(utilityStmt,
|
||||
portal->sourceText,
|
||||
portal->portalParams,
|
||||
isTopLevel,
|
||||
dest,
|
||||
completionTag);
|
||||
|
||||
/* Some utility statements may change context on us */
|
||||
MemoryContextSwitchTo(PortalGetHeapMemory(portal));
|
||||
@@ -1189,7 +1188,7 @@ PortalRunUtility(Portal portal, Node *utilityStmt,
|
||||
* or non-SELECT-like queries)
|
||||
*/
|
||||
static void
|
||||
PortalRunMulti(Portal portal,
|
||||
PortalRunMulti(Portal portal, bool isTopLevel,
|
||||
DestReceiver *dest, DestReceiver *altdest,
|
||||
char *completionTag)
|
||||
{
|
||||
@@ -1260,9 +1259,9 @@ PortalRunMulti(Portal portal,
|
||||
* portal.
|
||||
*/
|
||||
if (list_length(portal->stmts) == 1)
|
||||
PortalRunUtility(portal, stmt, dest, completionTag);
|
||||
PortalRunUtility(portal, stmt, isTopLevel, dest, completionTag);
|
||||
else
|
||||
PortalRunUtility(portal, stmt, altdest, NULL);
|
||||
PortalRunUtility(portal, stmt, isTopLevel, altdest, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1305,6 +1304,8 @@ PortalRunMulti(Portal portal,
|
||||
* PortalRunFetch
|
||||
* Variant form of PortalRun that supports SQL FETCH directions.
|
||||
*
|
||||
* Note: we presently assume that no callers of this want isTopLevel = true.
|
||||
*
|
||||
* Returns number of rows processed (suitable for use in result tag)
|
||||
*/
|
||||
long
|
||||
@@ -1318,7 +1319,6 @@ PortalRunFetch(Portal portal,
|
||||
Snapshot saveActiveSnapshot;
|
||||
ResourceOwner saveResourceOwner;
|
||||
MemoryContext savePortalContext;
|
||||
MemoryContext saveQueryContext;
|
||||
MemoryContext oldContext;
|
||||
|
||||
AssertArg(PortalIsValid(portal));
|
||||
@@ -1339,14 +1339,12 @@ PortalRunFetch(Portal portal,
|
||||
saveActiveSnapshot = ActiveSnapshot;
|
||||
saveResourceOwner = CurrentResourceOwner;
|
||||
savePortalContext = PortalContext;
|
||||
saveQueryContext = QueryContext;
|
||||
PG_TRY();
|
||||
{
|
||||
ActivePortal = portal;
|
||||
ActiveSnapshot = NULL; /* will be set later */
|
||||
CurrentResourceOwner = portal->resowner;
|
||||
PortalContext = PortalGetHeapMemory(portal);
|
||||
QueryContext = portal->queryContext;
|
||||
|
||||
oldContext = MemoryContextSwitchTo(PortalContext);
|
||||
|
||||
@@ -1364,7 +1362,7 @@ PortalRunFetch(Portal portal,
|
||||
* results in the portal's tuplestore.
|
||||
*/
|
||||
if (!portal->holdStore)
|
||||
FillPortalStore(portal);
|
||||
FillPortalStore(portal, false /* isTopLevel */);
|
||||
|
||||
/*
|
||||
* Now fetch desired portion of results.
|
||||
@@ -1388,7 +1386,6 @@ PortalRunFetch(Portal portal,
|
||||
ActiveSnapshot = saveActiveSnapshot;
|
||||
CurrentResourceOwner = saveResourceOwner;
|
||||
PortalContext = savePortalContext;
|
||||
QueryContext = saveQueryContext;
|
||||
|
||||
PG_RE_THROW();
|
||||
}
|
||||
@@ -1403,7 +1400,6 @@ PortalRunFetch(Portal portal,
|
||||
ActiveSnapshot = saveActiveSnapshot;
|
||||
CurrentResourceOwner = saveResourceOwner;
|
||||
PortalContext = savePortalContext;
|
||||
QueryContext = saveQueryContext;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.273 2007/02/20 17:32:16 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.274 2007/03/13 00:33:42 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "commands/vacuum.h"
|
||||
#include "commands/view.h"
|
||||
#include "miscadmin.h"
|
||||
#include "parser/analyze.h"
|
||||
#include "postmaster/bgwriter.h"
|
||||
#include "rewrite/rewriteDefine.h"
|
||||
#include "rewrite/rewriteRemove.h"
|
||||
@@ -368,7 +369,9 @@ check_xact_readonly(Node *parsetree)
|
||||
* general utility function invoker
|
||||
*
|
||||
* parsetree: the parse tree for the utility statement
|
||||
* queryString: original source text of command (NULL if not available)
|
||||
* params: parameters to use during execution
|
||||
* isTopLevel: true if executing a "top level" (interactively issued) command
|
||||
* dest: where to send results
|
||||
* completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
|
||||
* in which to store a command completion status string.
|
||||
@@ -379,7 +382,9 @@ check_xact_readonly(Node *parsetree)
|
||||
*/
|
||||
void
|
||||
ProcessUtility(Node *parsetree,
|
||||
const char *queryString,
|
||||
ParamListInfo params,
|
||||
bool isTopLevel,
|
||||
DestReceiver *dest,
|
||||
char *completionTag)
|
||||
{
|
||||
@@ -444,12 +449,12 @@ ProcessUtility(Node *parsetree,
|
||||
break;
|
||||
|
||||
case TRANS_STMT_COMMIT_PREPARED:
|
||||
PreventTransactionChain(stmt, "COMMIT PREPARED");
|
||||
PreventTransactionChain(isTopLevel, "COMMIT PREPARED");
|
||||
FinishPreparedTransaction(stmt->gid, true);
|
||||
break;
|
||||
|
||||
case TRANS_STMT_ROLLBACK_PREPARED:
|
||||
PreventTransactionChain(stmt, "ROLLBACK PREPARED");
|
||||
PreventTransactionChain(isTopLevel, "ROLLBACK PREPARED");
|
||||
FinishPreparedTransaction(stmt->gid, false);
|
||||
break;
|
||||
|
||||
@@ -462,7 +467,7 @@ ProcessUtility(Node *parsetree,
|
||||
ListCell *cell;
|
||||
char *name = NULL;
|
||||
|
||||
RequireTransactionChain((void *) stmt, "SAVEPOINT");
|
||||
RequireTransactionChain(isTopLevel, "SAVEPOINT");
|
||||
|
||||
foreach(cell, stmt->options)
|
||||
{
|
||||
@@ -479,12 +484,12 @@ ProcessUtility(Node *parsetree,
|
||||
break;
|
||||
|
||||
case TRANS_STMT_RELEASE:
|
||||
RequireTransactionChain((void *) stmt, "RELEASE SAVEPOINT");
|
||||
RequireTransactionChain(isTopLevel, "RELEASE SAVEPOINT");
|
||||
ReleaseSavepoint(stmt->options);
|
||||
break;
|
||||
|
||||
case TRANS_STMT_ROLLBACK_TO:
|
||||
RequireTransactionChain((void *) stmt, "ROLLBACK TO SAVEPOINT");
|
||||
RequireTransactionChain(isTopLevel, "ROLLBACK TO SAVEPOINT");
|
||||
RollbackToSavepoint(stmt->options);
|
||||
|
||||
/*
|
||||
@@ -500,7 +505,8 @@ ProcessUtility(Node *parsetree,
|
||||
* Portal (cursor) manipulation
|
||||
*/
|
||||
case T_DeclareCursorStmt:
|
||||
PerformCursorOpen((DeclareCursorStmt *) parsetree, params);
|
||||
PerformCursorOpen((DeclareCursorStmt *) parsetree, params,
|
||||
queryString, isTopLevel);
|
||||
break;
|
||||
|
||||
case T_ClosePortalStmt:
|
||||
@@ -520,7 +526,8 @@ ProcessUtility(Node *parsetree,
|
||||
* relation and attribute manipulation
|
||||
*/
|
||||
case T_CreateSchemaStmt:
|
||||
CreateSchemaCommand((CreateSchemaStmt *) parsetree);
|
||||
CreateSchemaCommand((CreateSchemaStmt *) parsetree,
|
||||
queryString);
|
||||
break;
|
||||
|
||||
case T_CreateStmt:
|
||||
@@ -540,10 +547,12 @@ ProcessUtility(Node *parsetree,
|
||||
break;
|
||||
|
||||
case T_CreateTableSpaceStmt:
|
||||
PreventTransactionChain(isTopLevel, "CREATE TABLESPACE");
|
||||
CreateTableSpace((CreateTableSpaceStmt *) parsetree);
|
||||
break;
|
||||
|
||||
case T_DropTableSpaceStmt:
|
||||
PreventTransactionChain(isTopLevel, "DROP TABLESPACE");
|
||||
DropTableSpace((DropTableSpaceStmt *) parsetree);
|
||||
break;
|
||||
|
||||
@@ -640,8 +649,9 @@ ProcessUtility(Node *parsetree,
|
||||
|
||||
case T_CopyStmt:
|
||||
{
|
||||
uint64 processed = DoCopy((CopyStmt *) parsetree);
|
||||
uint64 processed;
|
||||
|
||||
processed = DoCopy((CopyStmt *) parsetree, queryString);
|
||||
if (completionTag)
|
||||
snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
|
||||
"COPY " UINT64_FORMAT, processed);
|
||||
@@ -649,11 +659,11 @@ ProcessUtility(Node *parsetree,
|
||||
break;
|
||||
|
||||
case T_PrepareStmt:
|
||||
PrepareQuery((PrepareStmt *) parsetree);
|
||||
PrepareQuery((PrepareStmt *) parsetree, queryString);
|
||||
break;
|
||||
|
||||
case T_ExecuteStmt:
|
||||
ExecuteQuery((ExecuteStmt *) parsetree, params,
|
||||
ExecuteQuery((ExecuteStmt *) parsetree, queryString, params,
|
||||
dest, completionTag);
|
||||
break;
|
||||
|
||||
@@ -769,12 +779,8 @@ ProcessUtility(Node *parsetree,
|
||||
}
|
||||
break;
|
||||
|
||||
case T_ViewStmt: /* CREATE VIEW */
|
||||
{
|
||||
ViewStmt *stmt = (ViewStmt *) parsetree;
|
||||
|
||||
DefineView(stmt->view, stmt->query, stmt->replace);
|
||||
}
|
||||
case T_ViewStmt: /* CREATE VIEW */
|
||||
DefineView((ViewStmt *) parsetree, queryString);
|
||||
break;
|
||||
|
||||
case T_CreateFunctionStmt: /* CREATE FUNCTION */
|
||||
@@ -790,10 +796,15 @@ ProcessUtility(Node *parsetree,
|
||||
IndexStmt *stmt = (IndexStmt *) parsetree;
|
||||
|
||||
if (stmt->concurrent)
|
||||
PreventTransactionChain(stmt, "CREATE INDEX CONCURRENTLY");
|
||||
PreventTransactionChain(isTopLevel,
|
||||
"CREATE INDEX CONCURRENTLY");
|
||||
|
||||
CheckRelationOwnership(stmt->relation, true);
|
||||
|
||||
/* Run parse analysis ... */
|
||||
stmt = analyzeIndexStmt(stmt, queryString);
|
||||
|
||||
/* ... and do it */
|
||||
DefineIndex(stmt->relation, /* relation */
|
||||
stmt->idxname, /* index name */
|
||||
InvalidOid, /* no predefined OID */
|
||||
@@ -801,7 +812,6 @@ ProcessUtility(Node *parsetree,
|
||||
stmt->tableSpace,
|
||||
stmt->indexParams, /* parameters */
|
||||
(Expr *) stmt->whereClause,
|
||||
stmt->rangetable,
|
||||
stmt->options,
|
||||
stmt->unique,
|
||||
stmt->primary,
|
||||
@@ -815,7 +825,7 @@ ProcessUtility(Node *parsetree,
|
||||
break;
|
||||
|
||||
case T_RuleStmt: /* CREATE RULE */
|
||||
DefineQueryRewrite((RuleStmt *) parsetree);
|
||||
DefineRule((RuleStmt *) parsetree, queryString);
|
||||
break;
|
||||
|
||||
case T_CreateSeqStmt:
|
||||
@@ -850,6 +860,7 @@ ProcessUtility(Node *parsetree,
|
||||
break;
|
||||
|
||||
case T_CreatedbStmt:
|
||||
PreventTransactionChain(isTopLevel, "CREATE DATABASE");
|
||||
createdb((CreatedbStmt *) parsetree);
|
||||
break;
|
||||
|
||||
@@ -865,6 +876,7 @@ ProcessUtility(Node *parsetree,
|
||||
{
|
||||
DropdbStmt *stmt = (DropdbStmt *) parsetree;
|
||||
|
||||
PreventTransactionChain(isTopLevel, "DROP DATABASE");
|
||||
dropdb(stmt->dbname, stmt->missing_ok);
|
||||
}
|
||||
break;
|
||||
@@ -905,15 +917,15 @@ ProcessUtility(Node *parsetree,
|
||||
break;
|
||||
|
||||
case T_ClusterStmt:
|
||||
cluster((ClusterStmt *) parsetree);
|
||||
cluster((ClusterStmt *) parsetree, isTopLevel);
|
||||
break;
|
||||
|
||||
case T_VacuumStmt:
|
||||
vacuum((VacuumStmt *) parsetree, NIL);
|
||||
vacuum((VacuumStmt *) parsetree, NIL, isTopLevel);
|
||||
break;
|
||||
|
||||
case T_ExplainStmt:
|
||||
ExplainQuery((ExplainStmt *) parsetree, params, dest);
|
||||
ExplainQuery((ExplainStmt *) parsetree, queryString, params, dest);
|
||||
break;
|
||||
|
||||
case T_VariableSetStmt:
|
||||
@@ -1079,6 +1091,14 @@ ProcessUtility(Node *parsetree,
|
||||
ReindexTable(stmt->relation);
|
||||
break;
|
||||
case OBJECT_DATABASE:
|
||||
/*
|
||||
* This cannot run inside a user transaction block;
|
||||
* if we were inside a transaction, then its commit-
|
||||
* and start-transaction-command calls would not have
|
||||
* the intended effect!
|
||||
*/
|
||||
PreventTransactionChain(isTopLevel,
|
||||
"REINDEX DATABASE");
|
||||
ReindexDatabase(stmt->name,
|
||||
stmt->do_system, stmt->do_user);
|
||||
break;
|
||||
@@ -1166,16 +1186,8 @@ UtilityReturnsTuples(Node *parsetree)
|
||||
entry = FetchPreparedStatement(stmt->name, false);
|
||||
if (!entry)
|
||||
return false; /* not our business to raise error */
|
||||
switch (ChoosePortalStrategy(entry->stmt_list))
|
||||
{
|
||||
case PORTAL_ONE_SELECT:
|
||||
case PORTAL_ONE_RETURNING:
|
||||
case PORTAL_UTIL_SELECT:
|
||||
return true;
|
||||
case PORTAL_MULTI_QUERY:
|
||||
/* will not return tuples */
|
||||
break;
|
||||
}
|
||||
if (entry->plansource->resultDesc)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2134,7 +2146,7 @@ GetCommandLogLevel(Node *parsetree)
|
||||
|
||||
/* Look through an EXPLAIN ANALYZE to the contained stmt */
|
||||
if (stmt->analyze)
|
||||
return GetCommandLogLevel((Node *) stmt->query);
|
||||
return GetCommandLogLevel(stmt->query);
|
||||
/* Plain EXPLAIN isn't so interesting */
|
||||
lev = LOGSTMT_ALL;
|
||||
}
|
||||
@@ -2245,30 +2257,21 @@ GetCommandLogLevel(Node *parsetree)
|
||||
PrepareStmt *stmt = (PrepareStmt *) parsetree;
|
||||
|
||||
/* Look through a PREPARE to the contained stmt */
|
||||
return GetCommandLogLevel((Node *) stmt->query);
|
||||
lev = GetCommandLogLevel(stmt->query);
|
||||
}
|
||||
break;
|
||||
|
||||
case T_ExecuteStmt:
|
||||
{
|
||||
ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
|
||||
PreparedStatement *pstmt;
|
||||
ListCell *l;
|
||||
PreparedStatement *ps;
|
||||
|
||||
/* Look through an EXECUTE to the referenced stmt(s) */
|
||||
lev = LOGSTMT_ALL;
|
||||
pstmt = FetchPreparedStatement(stmt->name, false);
|
||||
if (pstmt)
|
||||
{
|
||||
foreach(l, pstmt->stmt_list)
|
||||
{
|
||||
Node *substmt = (Node *) lfirst(l);
|
||||
LogStmtLevel stmt_lev;
|
||||
|
||||
stmt_lev = GetCommandLogLevel(substmt);
|
||||
lev = Min(lev, stmt_lev);
|
||||
}
|
||||
}
|
||||
/* Look through an EXECUTE to the referenced stmt */
|
||||
ps = FetchPreparedStatement(stmt->name, false);
|
||||
if (ps)
|
||||
lev = GetCommandLogLevel(ps->plansource->raw_parse_tree);
|
||||
else
|
||||
lev = LOGSTMT_ALL;
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user