1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-05 07:21:24 +03:00

Remove the Query structure from the executor's API. This allows us to stop

storing mostly-redundant Query trees in prepared statements, portals, etc.
To replace Query, a new node type called PlannedStmt is inserted by the
planner at the top of a completed plan tree; this carries just the fields of
Query that are still needed at runtime.  The statement lists kept in portals
etc. now consist of intermixed PlannedStmt and bare utility-statement nodes
--- no Query.  This incidentally allows us to remove some fields from Query
and Plan nodes that shouldn't have been there in the first place.

Still to do: simplify the execution-time range table; at the moment the
range table passed to the executor still contains Query trees for subqueries.

initdb forced due to change of stored rules.
This commit is contained in:
Tom Lane
2007-02-20 17:32:18 +00:00
parent 71b0cf2f6b
commit 9cbd0c155d
39 changed files with 1172 additions and 897 deletions

View File

@ -10,7 +10,7 @@
* Copyright (c) 2002-2007, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.68 2007/01/28 19:05:35 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.69 2007/02/20 17:32:14 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -114,9 +114,9 @@ PrepareQuery(PrepareStmt *stmt)
StorePreparedStatement(stmt->name,
debug_query_string,
commandTag,
query_list,
plan_list,
stmt->argtype_oids,
true,
true);
}
@ -129,8 +129,7 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params,
{
PreparedStatement *entry;
char *query_string;
List *query_list,
*plan_list;
List *plan_list;
MemoryContext qcontext;
ParamListInfo paramLI = NULL;
EState *estate = NULL;
@ -139,12 +138,17 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params,
/* Look it up in the hash table */
entry = FetchPreparedStatement(stmt->name, true);
query_string = entry->query_string;
query_list = entry->query_list;
plan_list = entry->plan_list;
qcontext = entry->context;
/*
* Punt if not fully planned. (Currently, that only happens for the
* protocol-level unnamed statement, which can't be accessed from SQL;
* so there's no point in doing more than a quick check here.)
*/
if (!entry->fully_planned)
elog(ERROR, "EXECUTE does not support unplanned prepared statements");
Assert(list_length(query_list) == list_length(plan_list));
query_string = entry->query_string;
plan_list = entry->stmt_list;
qcontext = entry->context;
/* Evaluate parameters, if any */
if (entry->argtype_list != NIL)
@ -172,30 +176,26 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params,
if (stmt->into)
{
MemoryContext oldContext;
Query *query;
PlannedStmt *pstmt;
oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
qcontext = PortalGetHeapMemory(portal);
oldContext = MemoryContextSwitchTo(qcontext);
if (query_string)
query_string = pstrdup(query_string);
query_list = copyObject(query_list);
plan_list = copyObject(plan_list);
qcontext = PortalGetHeapMemory(portal);
if (list_length(query_list) != 1)
if (list_length(plan_list) != 1)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("prepared statement is not a SELECT")));
query = (Query *) linitial(query_list);
if (query->commandType != CMD_SELECT)
pstmt = (PlannedStmt *) linitial(plan_list);
if (!IsA(pstmt, PlannedStmt) ||
pstmt->commandType != CMD_SELECT)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("prepared statement is not a SELECT")));
query->into = copyObject(stmt->into);
query->intoOptions = copyObject(stmt->intoOptions);
query->intoOnCommit = stmt->into_on_commit;
if (stmt->into_tbl_space)
query->intoTableSpaceName = pstrdup(stmt->into_tbl_space);
pstmt->into = copyObject(stmt->into);
MemoryContextSwitchTo(oldContext);
}
@ -204,7 +204,6 @@ ExecuteQuery(ExecuteStmt *stmt, ParamListInfo params,
NULL,
query_string,
entry->commandTag,
query_list,
plan_list,
qcontext);
@ -305,9 +304,9 @@ void
StorePreparedStatement(const char *stmt_name,
const char *query_string,
const char *commandTag,
List *query_list,
List *plan_list,
List *stmt_list,
List *argtype_list,
bool fully_planned,
bool from_sql)
{
PreparedStatement *entry;
@ -345,8 +344,7 @@ StorePreparedStatement(const char *stmt_name,
* incomplete (ie corrupt) hashtable entry.
*/
qstring = query_string ? pstrdup(query_string) : NULL;
query_list = (List *) copyObject(query_list);
plan_list = (List *) copyObject(plan_list);
stmt_list = (List *) copyObject(stmt_list);
argtype_list = list_copy(argtype_list);
/* Now we can add entry to hash table */
@ -363,12 +361,12 @@ StorePreparedStatement(const char *stmt_name,
/* Fill in the hash table entry with copied data */
entry->query_string = qstring;
entry->commandTag = commandTag;
entry->query_list = query_list;
entry->plan_list = plan_list;
entry->stmt_list = stmt_list;
entry->argtype_list = argtype_list;
entry->fully_planned = fully_planned;
entry->from_sql = from_sql;
entry->context = entrycxt;
entry->prepare_time = GetCurrentStatementStartTimestamp();
entry->from_sql = from_sql;
MemoryContextSwitchTo(oldcxt);
}
@ -426,21 +424,54 @@ FetchPreparedStatementParams(const char *stmt_name)
TupleDesc
FetchPreparedStatementResultDesc(PreparedStatement *stmt)
{
Node *node;
Query *query;
PlannedStmt *pstmt;
switch (ChoosePortalStrategy(stmt->query_list))
switch (ChoosePortalStrategy(stmt->stmt_list))
{
case PORTAL_ONE_SELECT:
query = (Query *) linitial(stmt->query_list);
return ExecCleanTypeFromTL(query->targetList, false);
node = (Node *) linitial(stmt->stmt_list);
if (IsA(node, Query))
{
query = (Query *) node;
return ExecCleanTypeFromTL(query->targetList, false);
}
if (IsA(node, PlannedStmt))
{
pstmt = (PlannedStmt *) node;
return ExecCleanTypeFromTL(pstmt->planTree->targetlist, false);
}
/* other cases shouldn't happen, but return NULL */
break;
case PORTAL_ONE_RETURNING:
query = PortalListGetPrimaryQuery(stmt->query_list);
return ExecCleanTypeFromTL(query->returningList, false);
node = PortalListGetPrimaryStmt(stmt->stmt_list);
if (IsA(node, Query))
{
query = (Query *) node;
Assert(query->returningList);
return ExecCleanTypeFromTL(query->returningList, false);
}
if (IsA(node, PlannedStmt))
{
pstmt = (PlannedStmt *) node;
Assert(pstmt->returningLists);
return ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists), false);
}
/* other cases shouldn't happen, but return NULL */
break;
case PORTAL_UTIL_SELECT:
query = (Query *) linitial(stmt->query_list);
return UtilityTupleDescriptor(query->utilityStmt);
node = (Node *) linitial(stmt->stmt_list);
if (IsA(node, Query))
{
query = (Query *) node;
Assert(query->utilityStmt);
return UtilityTupleDescriptor(query->utilityStmt);
}
/* else it's a bare utility statement */
return UtilityTupleDescriptor(node);
case PORTAL_MULTI_QUERY:
/* will not return tuples */
@ -460,7 +491,7 @@ FetchPreparedStatementResultDesc(PreparedStatement *stmt)
bool
PreparedStatementReturnsTuples(PreparedStatement *stmt)
{
switch (ChoosePortalStrategy(stmt->query_list))
switch (ChoosePortalStrategy(stmt->stmt_list))
{
case PORTAL_ONE_SELECT:
case PORTAL_ONE_RETURNING:
@ -480,52 +511,15 @@ PreparedStatementReturnsTuples(PreparedStatement *stmt)
* targetlist.
*
* Note: do not modify the result.
*
* XXX be careful to keep this in sync with FetchPortalTargetList,
* and with UtilityReturnsTuples.
*/
List *
FetchPreparedStatementTargetList(PreparedStatement *stmt)
{
PortalStrategy strategy = ChoosePortalStrategy(stmt->query_list);
if (strategy == PORTAL_ONE_SELECT)
return ((Query *) linitial(stmt->query_list))->targetList;
if (strategy == PORTAL_ONE_RETURNING)
return (PortalListGetPrimaryQuery(stmt->query_list))->returningList;
if (strategy == PORTAL_UTIL_SELECT)
{
Node *utilityStmt;
utilityStmt = ((Query *) linitial(stmt->query_list))->utilityStmt;
switch (nodeTag(utilityStmt))
{
case T_FetchStmt:
{
FetchStmt *substmt = (FetchStmt *) utilityStmt;
Portal subportal;
Assert(!substmt->ismove);
subportal = GetPortalByName(substmt->portalname);
Assert(PortalIsValid(subportal));
return FetchPortalTargetList(subportal);
}
case T_ExecuteStmt:
{
ExecuteStmt *substmt = (ExecuteStmt *) utilityStmt;
PreparedStatement *entry;
Assert(!substmt->into);
entry = FetchPreparedStatement(substmt->name, true);
return FetchPreparedStatementTargetList(entry);
}
default:
break;
}
}
return NIL;
/* no point in looking if it doesn't return tuples */
if (ChoosePortalStrategy(stmt->stmt_list) == PORTAL_MULTI_QUERY)
return NIL;
/* get the primary statement and find out what it returns */
return FetchStatementTargetList(PortalListGetPrimaryStmt(stmt->stmt_list));
}
/*
@ -574,10 +568,8 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
{
ExecuteStmt *execstmt = (ExecuteStmt *) stmt->query->utilityStmt;
PreparedStatement *entry;
ListCell *q,
*p;
List *query_list,
*plan_list;
List *plan_list;
ListCell *p;
ParamListInfo paramLI = NULL;
EState *estate = NULL;
@ -587,10 +579,15 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
/* Look it up in the hash table */
entry = FetchPreparedStatement(execstmt->name, true);
query_list = entry->query_list;
plan_list = entry->plan_list;
/*
* Punt if not fully planned. (Currently, that only happens for the
* protocol-level unnamed statement, which can't be accessed from SQL;
* so there's no point in doing more than a quick check here.)
*/
if (!entry->fully_planned)
elog(ERROR, "EXPLAIN EXECUTE does not support unplanned prepared statements");
Assert(list_length(query_list) == list_length(plan_list));
plan_list = entry->stmt_list;
/* Evaluate parameters, if any */
if (entry->argtype_list != NIL)
@ -606,17 +603,16 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
}
/* Explain each query */
forboth(q, query_list, p, plan_list)
foreach(p, plan_list)
{
Query *query = (Query *) lfirst(q);
Plan *plan = (Plan *) lfirst(p);
PlannedStmt *pstmt = (PlannedStmt *) lfirst(p);
bool is_last_query;
is_last_query = (lnext(p) == NULL);
if (query->commandType == CMD_UTILITY)
if (!IsA(pstmt, PlannedStmt))
{
if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
if (IsA(pstmt, NotifyStmt))
do_text_output_oneline(tstate, "NOTIFY");
else
do_text_output_oneline(tstate, "UTILITY");
@ -627,15 +623,15 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
if (execstmt->into)
{
if (query->commandType != CMD_SELECT)
if (pstmt->commandType != CMD_SELECT)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("prepared statement is not a SELECT")));
/* Copy the query so we can modify it */
query = copyObject(query);
/* Copy the stmt so we can modify it */
pstmt = copyObject(pstmt);
query->into = execstmt->into;
pstmt->into = execstmt->into;
}
/*
@ -648,7 +644,7 @@ ExplainExecuteQuery(ExplainStmt *stmt, ParamListInfo params,
ActiveSnapshot->curcid = GetCurrentCommandId();
/* Create a QueryDesc requesting no output */
qdesc = CreateQueryDesc(query, plan,
qdesc = CreateQueryDesc(pstmt,
ActiveSnapshot, InvalidSnapshot,
None_Receiver,
paramLI, stmt->analyze);