1
0
mirror of https://github.com/postgres/postgres.git synced 2025-08-19 23:22:23 +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

@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.524 2007/02/17 19:33:32 tgl Exp $
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.525 2007/02/20 17:32:16 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
@@ -165,8 +165,7 @@ static int InteractiveBackend(StringInfo inBuf);
static int SocketBackend(StringInfo inBuf);
static int ReadCommand(StringInfo inBuf);
static List *pg_rewrite_queries(List *querytree_list);
static bool check_log_statement_raw(List *raw_parsetree_list);
static bool check_log_statement_cooked(List *parsetree_list);
static bool check_log_statement(List *stmt_list);
static int errdetail_execute(List *raw_parsetree_list);
static int errdetail_params(ParamListInfo params);
static void start_xact_command(void);
@@ -659,10 +658,10 @@ pg_rewrite_queries(List *querytree_list)
/* Generate a plan for a single already-rewritten query. */
Plan *
PlannedStmt *
pg_plan_query(Query *querytree, ParamListInfo boundParams)
{
Plan *plan;
PlannedStmt *plan;
/* Utility commands have no plans. */
if (querytree->commandType == CMD_UTILITY)
@@ -680,7 +679,7 @@ pg_plan_query(Query *querytree, ParamListInfo boundParams)
#ifdef COPY_PARSE_PLAN_TREES
/* Optional debugging check: pass plan output through copyObject() */
{
Plan *new_plan = (Plan *) copyObject(plan);
PlannedStmt *new_plan = (PlannedStmt *) copyObject(plan);
/*
* equal() currently does not have routines to compare Plan nodes, so
@@ -715,23 +714,26 @@ pg_plan_query(Query *querytree, ParamListInfo boundParams)
* utility statements depend on not having frozen the snapshot yet.
* (We assume that such statements cannot appear together with plannable
* statements in the rewriter's output.)
*
* Normal optimizable statements generate PlannedStmt entries in the result
* list. Utility statements are simply represented by their statement nodes.
*/
List *
pg_plan_queries(List *querytrees, ParamListInfo boundParams,
bool needSnapshot)
{
List *plan_list = NIL;
List *stmt_list = NIL;
ListCell *query_list;
foreach(query_list, querytrees)
{
Query *query = (Query *) lfirst(query_list);
Plan *plan;
Node *stmt;
if (query->commandType == CMD_UTILITY)
{
/* Utility commands have no plans. */
plan = NULL;
stmt = query->utilityStmt;
}
else
{
@@ -740,13 +742,13 @@ pg_plan_queries(List *querytrees, ParamListInfo boundParams,
ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
needSnapshot = false;
}
plan = pg_plan_query(query, boundParams);
stmt = (Node *) pg_plan_query(query, boundParams);
}
plan_list = lappend(plan_list, plan);
stmt_list = lappend(stmt_list, stmt);
}
return plan_list;
return stmt_list;
}
@@ -817,7 +819,7 @@ exec_simple_query(const char *query_string)
parsetree_list = pg_parse_query(query_string);
/* Log immediately if dictated by log_statement */
if (check_log_statement_raw(parsetree_list))
if (check_log_statement(parsetree_list))
{
ereport(LOG,
(errmsg("statement: %s", query_string),
@@ -905,7 +907,6 @@ exec_simple_query(const char *query_string)
NULL,
query_string,
commandTag,
querytree_list,
plantree_list,
MessageContext);
@@ -1050,9 +1051,10 @@ exec_parse_message(const char *query_string, /* string to execute */
List *parsetree_list;
const char *commandTag;
List *querytree_list,
*plantree_list,
*stmt_list,
*param_list;
bool is_named;
bool fully_planned;
bool save_log_statement_stats = log_statement_stats;
char msec_str[32];
@@ -1202,17 +1204,23 @@ exec_parse_message(const char *query_string, /* string to execute */
* planning until Bind. Otherwise do it now.
*/
if (!is_named && numParams > 0)
plantree_list = NIL;
{
stmt_list = querytree_list;
fully_planned = false;
}
else
plantree_list = pg_plan_queries(querytree_list, NULL, true);
{
stmt_list = pg_plan_queries(querytree_list, NULL, true);
fully_planned = true;
}
}
else
{
/* Empty input string. This is legal. */
commandTag = NULL;
querytree_list = NIL;
plantree_list = NIL;
stmt_list = NIL;
param_list = NIL;
fully_planned = true;
}
/* If we got a cancel signal in analysis or planning, quit */
@@ -1226,9 +1234,9 @@ exec_parse_message(const char *query_string, /* string to execute */
StorePreparedStatement(stmt_name,
query_string,
commandTag,
querytree_list,
plantree_list,
stmt_list,
param_list,
fully_planned,
false);
}
else
@@ -1240,9 +1248,9 @@ exec_parse_message(const char *query_string, /* string to execute */
pstmt->query_string = pstrdup(query_string);
/* the rest is there already */
pstmt->commandTag = commandTag;
pstmt->query_list = querytree_list;
pstmt->plan_list = plantree_list;
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 */
@@ -1393,7 +1401,7 @@ exec_bind_message(StringInfo input_message)
* functions.
*/
if (IsAbortedTransactionBlockState() &&
(!IsTransactionExitStmtList(pstmt->query_list) ||
(!IsTransactionExitStmtList(pstmt->stmt_list) ||
numParams != 0))
ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
@@ -1581,22 +1589,21 @@ exec_bind_message(StringInfo input_message)
* portal's queryContext becomes its own heap context rather than the
* prepared statement's context. FIXME someday
*/
if (pstmt->plan_list == NIL && pstmt->query_list != NIL)
if (pstmt->fully_planned)
{
plan_list = pstmt->stmt_list;
qContext = pstmt->context;
}
else
{
MemoryContext oldContext;
qContext = PortalGetHeapMemory(portal);
oldContext = MemoryContextSwitchTo(qContext);
query_list = copyObject(pstmt->query_list);
query_list = copyObject(pstmt->stmt_list);
plan_list = pg_plan_queries(query_list, params, true);
MemoryContextSwitchTo(oldContext);
}
else
{
query_list = pstmt->query_list;
plan_list = pstmt->plan_list;
qContext = pstmt->context;
}
/*
* Define portal and start execution.
@@ -1605,7 +1612,6 @@ exec_bind_message(StringInfo input_message)
*pstmt->stmt_name ? pstmt->stmt_name : NULL,
pstmt->query_string,
pstmt->commandTag,
query_list,
plan_list,
qContext);
@@ -1688,13 +1694,13 @@ exec_execute_message(const char *portal_name, long max_rows)
*/
if (portal->commandTag == NULL)
{
Assert(portal->parseTrees == NIL);
Assert(portal->stmts == NIL);
NullCommand(dest);
return;
}
/* Does the portal contain a transaction command? */
is_xact_command = IsTransactionStmtList(portal->parseTrees);
is_xact_command = IsTransactionStmtList(portal->stmts);
/*
* We must copy the sourceText and prepStmtName into MessageContext in
@@ -1760,7 +1766,7 @@ exec_execute_message(const char *portal_name, long max_rows)
execute_is_fetch = !portal->atStart;
/* Log immediately if dictated by log_statement */
if (check_log_statement_cooked(portal->parseTrees))
if (check_log_statement(portal->stmts))
{
ereport(LOG,
(errmsg("%s %s%s%s%s%s",
@@ -1781,7 +1787,7 @@ exec_execute_message(const char *portal_name, long max_rows)
* actually run are those containing COMMIT or ROLLBACK commands.
*/
if (IsAbortedTransactionBlockState() &&
!IsTransactionExitStmtList(portal->parseTrees))
!IsTransactionExitStmtList(portal->stmts))
ereport(ERROR,
(errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION),
errmsg("current transaction is aborted, "
@@ -1865,15 +1871,16 @@ exec_execute_message(const char *portal_name, long max_rows)
}
/*
* check_log_statement_raw
* check_log_statement
* Determine whether command should be logged because of log_statement
*
* raw_parsetree_list is the raw grammar output
* parsetree_list can be either raw grammar output or a list of planned
* statements
*/
static bool
check_log_statement_raw(List *raw_parsetree_list)
check_log_statement(List *stmt_list)
{
ListCell *parsetree_item;
ListCell *stmt_item;
if (log_statement == LOGSTMT_NONE)
return false;
@@ -1881,37 +1888,11 @@ check_log_statement_raw(List *raw_parsetree_list)
return true;
/* Else we have to inspect the statement(s) to see whether to log */
foreach(parsetree_item, raw_parsetree_list)
foreach(stmt_item, stmt_list)
{
Node *parsetree = (Node *) lfirst(parsetree_item);
Node *stmt = (Node *) lfirst(stmt_item);
if (GetCommandLogLevel(parsetree) <= log_statement)
return true;
}
return false;
}
/*
* check_log_statement_cooked
* As above, but work from already-analyzed querytrees
*/
static bool
check_log_statement_cooked(List *parsetree_list)
{
ListCell *parsetree_item;
if (log_statement == LOGSTMT_NONE)
return false;
if (log_statement == LOGSTMT_ALL)
return true;
/* Else we have to inspect the statement(s) to see whether to log */
foreach(parsetree_item, parsetree_list)
{
Query *parsetree = (Query *) lfirst(parsetree_item);
if (GetQueryLogLevel(parsetree) <= log_statement)
if (GetCommandLogLevel(stmt) <= log_statement)
return true;
}
@@ -2259,6 +2240,7 @@ finish_xact_command(void)
* ones that we allow in transaction-aborted state.
*/
/* Test a bare parsetree */
static bool
IsTransactionExitStmt(Node *parsetree)
{
@@ -2275,29 +2257,45 @@ IsTransactionExitStmt(Node *parsetree)
return false;
}
/* Test a list that might contain Query nodes or bare parsetrees */
static bool
IsTransactionExitStmtList(List *parseTrees)
{
if (list_length(parseTrees) == 1)
{
Query *query = (Query *) linitial(parseTrees);
Node *stmt = (Node *) linitial(parseTrees);
if (query->commandType == CMD_UTILITY &&
IsTransactionExitStmt(query->utilityStmt))
if (IsA(stmt, Query))
{
Query *query = (Query *) stmt;
if (query->commandType == CMD_UTILITY &&
IsTransactionExitStmt(query->utilityStmt))
return true;
}
else if (IsTransactionExitStmt(stmt))
return true;
}
return false;
}
/* Test a list that might contain Query nodes or bare parsetrees */
static bool
IsTransactionStmtList(List *parseTrees)
{
if (list_length(parseTrees) == 1)
{
Query *query = (Query *) linitial(parseTrees);
Node *stmt = (Node *) linitial(parseTrees);
if (query->commandType == CMD_UTILITY &&
query->utilityStmt && IsA(query->utilityStmt, TransactionStmt))
if (IsA(stmt, Query))
{
Query *query = (Query *) stmt;
if (query->commandType == CMD_UTILITY &&
IsA(query->utilityStmt, TransactionStmt))
return true;
}
else if (IsA(stmt, TransactionStmt))
return true;
}
return false;