aboutsummaryrefslogtreecommitdiff
path: root/src/backend/tcop
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/tcop')
-rw-r--r--src/backend/tcop/postgres.c150
-rw-r--r--src/backend/tcop/pquery.c338
-rw-r--r--src/backend/tcop/utility.c310
3 files changed, 476 insertions, 322 deletions
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index f00897ee622..1c40a8752e9 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -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 (IsA(stmt, Query))
+ {
+ Query *query = (Query *) stmt;
- if (query->commandType == CMD_UTILITY &&
- IsTransactionExitStmt(query->utilityStmt))
+ 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;
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index f6f157e0cbb..97a003ac89d 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.113 2007/02/18 19:49:25 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/pquery.c,v 1.114 2007/02/20 17:32:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -32,8 +32,7 @@
Portal ActivePortal = NULL;
-static void ProcessQuery(Query *parsetree,
- Plan *plan,
+static void ProcessQuery(PlannedStmt *plan,
ParamListInfo params,
DestReceiver *dest,
char *completionTag);
@@ -42,7 +41,7 @@ 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, Query *query,
+static void PortalRunUtility(Portal portal, Node *utilityStmt,
DestReceiver *dest, char *completionTag);
static void PortalRunMulti(Portal portal,
DestReceiver *dest, DestReceiver *altdest,
@@ -58,8 +57,7 @@ static void DoPortalRewind(Portal portal);
* CreateQueryDesc
*/
QueryDesc *
-CreateQueryDesc(Query *parsetree,
- Plan *plantree,
+CreateQueryDesc(PlannedStmt *plannedstmt,
Snapshot snapshot,
Snapshot crosscheck_snapshot,
DestReceiver *dest,
@@ -68,10 +66,10 @@ CreateQueryDesc(Query *parsetree,
{
QueryDesc *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
- qd->operation = parsetree->commandType; /* operation */
- qd->parsetree = parsetree; /* parse tree */
- qd->plantree = plantree; /* plan */
- qd->snapshot = snapshot; /* snapshot */
+ qd->operation = plannedstmt->commandType; /* operation */
+ qd->plannedstmt = plannedstmt; /* plan */
+ qd->utilitystmt = NULL;
+ qd->snapshot = snapshot; /* snapshot */
qd->crosscheck_snapshot = crosscheck_snapshot; /* RI check snapshot */
qd->dest = dest; /* output dest */
qd->params = params; /* parameter values passed into query */
@@ -86,6 +84,34 @@ CreateQueryDesc(Query *parsetree,
}
/*
+ * CreateUtilityQueryDesc
+ */
+QueryDesc *
+CreateUtilityQueryDesc(Node *utilitystmt,
+ Snapshot snapshot,
+ DestReceiver *dest,
+ ParamListInfo params)
+{
+ QueryDesc *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
+
+ qd->operation = CMD_UTILITY; /* operation */
+ qd->plannedstmt = NULL;
+ qd->utilitystmt = utilitystmt; /* utility command */
+ qd->snapshot = snapshot; /* snapshot */
+ qd->crosscheck_snapshot = InvalidSnapshot; /* RI check snapshot */
+ qd->dest = dest; /* output dest */
+ qd->params = params; /* parameter values passed into query */
+ qd->doInstrument = false; /* uninteresting for utilities */
+
+ /* null these fields until set by ExecutorStart */
+ qd->tupDesc = NULL;
+ qd->estate = NULL;
+ qd->planstate = NULL;
+
+ return qd;
+}
+
+/*
* FreeQueryDesc
*/
void
@@ -103,7 +129,6 @@ FreeQueryDesc(QueryDesc *qdesc)
* Execute a single plannable query within a PORTAL_MULTI_QUERY
* or PORTAL_ONE_RETURNING portal
*
- * parsetree: the query tree
* plan: the plan tree for the query
* params: any parameters needed
* dest: where to send results
@@ -116,13 +141,11 @@ FreeQueryDesc(QueryDesc *qdesc)
* error; otherwise the executor's memory usage will be leaked.
*/
static void
-ProcessQuery(Query *parsetree,
- Plan *plan,
+ProcessQuery(PlannedStmt *plan,
ParamListInfo params,
DestReceiver *dest,
char *completionTag)
{
- int operation = parsetree->commandType;
QueryDesc *queryDesc;
ereport(DEBUG3,
@@ -137,7 +160,7 @@ ProcessQuery(Query *parsetree,
/*
* Create the QueryDesc object
*/
- queryDesc = CreateQueryDesc(parsetree, plan,
+ queryDesc = CreateQueryDesc(plan,
ActiveSnapshot, InvalidSnapshot,
dest, params, false);
@@ -163,7 +186,7 @@ ProcessQuery(Query *parsetree,
{
Oid lastOid;
- switch (operation)
+ switch (queryDesc->operation)
{
case CMD_SELECT:
strcpy(completionTag, "SELECT");
@@ -206,40 +229,67 @@ ProcessQuery(Query *parsetree,
/*
* ChoosePortalStrategy
- * Select portal execution strategy given the intended query list.
+ * 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.
*
* See the comments in portal.h.
*/
PortalStrategy
-ChoosePortalStrategy(List *parseTrees)
+ChoosePortalStrategy(List *stmts)
{
int nSetTag;
ListCell *lc;
/*
* PORTAL_ONE_SELECT and PORTAL_UTIL_SELECT need only consider the
- * single-Query-struct case, since there are no rewrite rules that can add
+ * single-statement case, since there are no rewrite rules that can add
* auxiliary queries to a SELECT or a utility command.
*/
- if (list_length(parseTrees) == 1)
+ if (list_length(stmts) == 1)
{
- Query *query = (Query *) linitial(parseTrees);
+ Node *stmt = (Node *) linitial(stmts);
- Assert(IsA(query, Query));
- if (query->canSetTag)
+ if (IsA(stmt, Query))
{
- if (query->commandType == CMD_SELECT &&
- query->into == NULL)
- return PORTAL_ONE_SELECT;
- if (query->commandType == CMD_UTILITY &&
- query->utilityStmt != NULL)
+ Query *query = (Query *) stmt;
+
+ if (query->canSetTag)
{
- if (UtilityReturnsTuples(query->utilityStmt))
- return PORTAL_UTIL_SELECT;
- /* it can't be ONE_RETURNING, so give up */
- return PORTAL_MULTI_QUERY;
+ if (query->commandType == CMD_SELECT &&
+ query->into == NULL)
+ return PORTAL_ONE_SELECT;
+ if (query->commandType == CMD_UTILITY &&
+ query->utilityStmt != NULL)
+ {
+ if (UtilityReturnsTuples(query->utilityStmt))
+ return PORTAL_UTIL_SELECT;
+ /* it can't be ONE_RETURNING, so give up */
+ return PORTAL_MULTI_QUERY;
+ }
}
}
+ else if (IsA(stmt, PlannedStmt))
+ {
+ PlannedStmt *pstmt = (PlannedStmt *) stmt;
+
+ if (pstmt->canSetTag)
+ {
+ if (pstmt->commandType == CMD_SELECT &&
+ pstmt->into == NULL)
+ return PORTAL_ONE_SELECT;
+ }
+ }
+ else
+ {
+ /* must be a utility command; assume it's canSetTag */
+ if (UtilityReturnsTuples(stmt))
+ return PORTAL_UTIL_SELECT;
+ /* it can't be ONE_RETURNING, so give up */
+ return PORTAL_MULTI_QUERY;
+ }
}
/*
@@ -248,18 +298,35 @@ ChoosePortalStrategy(List *parseTrees)
* it has a RETURNING list.
*/
nSetTag = 0;
- foreach(lc, parseTrees)
+ foreach(lc, stmts)
{
- Query *query = (Query *) lfirst(lc);
+ Node *stmt = (Node *) lfirst(lc);
- Assert(IsA(query, Query));
- if (query->canSetTag)
+ if (IsA(stmt, Query))
{
- if (++nSetTag > 1)
- return PORTAL_MULTI_QUERY; /* no need to look further */
- if (query->returningList == NIL)
- return PORTAL_MULTI_QUERY; /* no need to look further */
+ Query *query = (Query *) stmt;
+
+ if (query->canSetTag)
+ {
+ if (++nSetTag > 1)
+ return PORTAL_MULTI_QUERY; /* no need to look further */
+ if (query->returningList == NIL)
+ return PORTAL_MULTI_QUERY; /* no need to look further */
+ }
}
+ else if (IsA(stmt, PlannedStmt))
+ {
+ PlannedStmt *pstmt = (PlannedStmt *) stmt;
+
+ if (pstmt->canSetTag)
+ {
+ if (++nSetTag > 1)
+ return PORTAL_MULTI_QUERY; /* no need to look further */
+ if (pstmt->returningLists == NIL)
+ return PORTAL_MULTI_QUERY; /* no need to look further */
+ }
+ }
+ /* otherwise, utility command, assumed not canSetTag */
}
if (nSetTag == 1)
return PORTAL_ONE_RETURNING;
@@ -274,48 +341,84 @@ ChoosePortalStrategy(List *parseTrees)
* Returns NIL if the portal doesn't have a determinable targetlist.
*
* Note: do not modify the result.
- *
- * XXX be careful to keep this in sync with FetchPreparedStatementTargetList,
- * and with UtilityReturnsTuples.
*/
List *
FetchPortalTargetList(Portal portal)
{
- if (portal->strategy == PORTAL_ONE_SELECT)
- return ((Query *) linitial(portal->parseTrees))->targetList;
- if (portal->strategy == PORTAL_ONE_RETURNING)
- return (PortalGetPrimaryQuery(portal))->returningList;
- if (portal->strategy == PORTAL_UTIL_SELECT)
+ /* no point in looking if we determined it doesn't return tuples */
+ if (portal->strategy == PORTAL_MULTI_QUERY)
+ return NIL;
+ /* get the primary statement and find out what it returns */
+ return FetchStatementTargetList(PortalGetPrimaryStmt(portal));
+}
+
+/*
+ * FetchStatementTargetList
+ * Given a statement that returns tuples, extract the query targetlist.
+ * 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.
+ *
+ * Note: do not modify the result.
+ *
+ * XXX be careful to keep this in sync with UtilityReturnsTuples.
+ */
+List *
+FetchStatementTargetList(Node *stmt)
+{
+ if (stmt == NULL)
+ return NIL;
+ if (IsA(stmt, Query))
{
- Node *utilityStmt;
+ Query *query = (Query *) stmt;
- utilityStmt = ((Query *) linitial(portal->parseTrees))->utilityStmt;
- switch (nodeTag(utilityStmt))
+ if (query->commandType == CMD_UTILITY &&
+ query->utilityStmt != NULL)
{
- 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;
+ /* transfer attention to utility statement */
+ stmt = query->utilityStmt;
+ }
+ else
+ {
+ if (query->commandType == CMD_SELECT &&
+ query->into == NULL)
+ return query->targetList;
+ if (query->returningList)
+ return query->returningList;
+ return NIL;
+ }
+ }
+ if (IsA(stmt, PlannedStmt))
+ {
+ PlannedStmt *pstmt = (PlannedStmt *) stmt;
+
+ if (pstmt->commandType == CMD_SELECT &&
+ pstmt->into == NULL)
+ return pstmt->planTree->targetlist;
+ if (pstmt->returningLists)
+ return (List *) linitial(pstmt->returningLists);
+ return NIL;
+ }
+ if (IsA(stmt, FetchStmt))
+ {
+ FetchStmt *fstmt = (FetchStmt *) stmt;
+ Portal subportal;
- Assert(!substmt->into);
- entry = FetchPreparedStatement(substmt->name, true);
- return FetchPreparedStatementTargetList(entry);
- }
+ Assert(!fstmt->ismove);
+ subportal = GetPortalByName(fstmt->portalname);
+ Assert(PortalIsValid(subportal));
+ return FetchPortalTargetList(subportal);
+ }
+ if (IsA(stmt, ExecuteStmt))
+ {
+ ExecuteStmt *estmt = (ExecuteStmt *) stmt;
+ PreparedStatement *entry;
- default:
- break;
- }
+ Assert(!estmt->into);
+ entry = FetchPreparedStatement(estmt->name, true);
+ return FetchPreparedStatementTargetList(entry);
}
return NIL;
}
@@ -374,7 +477,7 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
/*
* Determine the portal execution strategy
*/
- portal->strategy = ChoosePortalStrategy(portal->parseTrees);
+ portal->strategy = ChoosePortalStrategy(portal->stmts);
/*
* Fire her up according to the strategy
@@ -396,8 +499,7 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
* Create QueryDesc in portal's context; for the moment, set
* the destination to DestNone.
*/
- queryDesc = CreateQueryDesc((Query *) linitial(portal->parseTrees),
- (Plan *) linitial(portal->planTrees),
+ queryDesc = CreateQueryDesc((PlannedStmt *) linitial(portal->stmts),
ActiveSnapshot,
InvalidSnapshot,
None_Receiver,
@@ -450,8 +552,16 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
* We don't start the executor until we are told to run the
* portal. We do need to set up the result tupdesc.
*/
- portal->tupDesc =
- ExecCleanTypeFromTL((PortalGetPrimaryQuery(portal))->returningList, false);
+ {
+ PlannedStmt *pstmt;
+
+ pstmt = (PlannedStmt *) PortalGetPrimaryStmt(portal);
+ Assert(IsA(pstmt, PlannedStmt));
+ Assert(pstmt->returningLists);
+ portal->tupDesc =
+ ExecCleanTypeFromTL((List *) linitial(pstmt->returningLists),
+ false);
+ }
/*
* Reset cursor position data to "start of query"
@@ -468,8 +578,12 @@ PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot)
* We don't set snapshot here, because PortalRunUtility will
* take care of it if needed.
*/
- portal->tupDesc =
- UtilityTupleDescriptor(((Query *) linitial(portal->parseTrees))->utilityStmt);
+ {
+ Node *ustmt = PortalGetPrimaryStmt(portal);
+
+ Assert(!IsA(ustmt, PlannedStmt));
+ portal->tupDesc = UtilityTupleDescriptor(ustmt);
+ }
/*
* Reset cursor position data to "start of query"
@@ -934,7 +1048,7 @@ FillPortalStore(Portal portal)
break;
case PORTAL_UTIL_SELECT:
- PortalRunUtility(portal, linitial(portal->parseTrees),
+ PortalRunUtility(portal, (Node *) linitial(portal->stmts),
treceiver, completionTag);
break;
@@ -1023,11 +1137,9 @@ RunFromStore(Portal portal, ScanDirection direction, long count,
* Execute a utility statement inside a portal.
*/
static void
-PortalRunUtility(Portal portal, Query *query,
+PortalRunUtility(Portal portal, Node *utilityStmt,
DestReceiver *dest, char *completionTag)
{
- Node *utilityStmt = query->utilityStmt;
-
ereport(DEBUG3,
(errmsg_internal("ProcessUtility")));
@@ -1061,18 +1173,7 @@ PortalRunUtility(Portal portal, Query *query,
else
ActiveSnapshot = NULL;
- if (query->canSetTag)
- {
- /* utility statement can override default tag string */
- ProcessUtility(utilityStmt, portal->portalParams, dest, completionTag);
- if (completionTag && completionTag[0] == '\0' && portal->commandTag)
- strcpy(completionTag, portal->commandTag); /* use the default */
- }
- else
- {
- /* utility added by rewrite cannot set tag */
- ProcessUtility(utilityStmt, portal->portalParams, dest, NULL);
- }
+ ProcessUtility(utilityStmt, portal->portalParams, dest, completionTag);
/* Some utility statements may change context on us */
MemoryContextSwitchTo(PortalGetHeapMemory(portal));
@@ -1092,8 +1193,7 @@ PortalRunMulti(Portal portal,
DestReceiver *dest, DestReceiver *altdest,
char *completionTag)
{
- ListCell *querylist_item;
- ListCell *planlist_item;
+ ListCell *stmtlist_item;
/*
* If the destination is DestRemoteExecute, change to DestNone. The
@@ -1114,47 +1214,36 @@ PortalRunMulti(Portal portal,
* Loop to handle the individual queries generated from a single parsetree
* by analysis and rewrite.
*/
- forboth(querylist_item, portal->parseTrees,
- planlist_item, portal->planTrees)
+ foreach(stmtlist_item, portal->stmts)
{
- Query *query = (Query *) lfirst(querylist_item);
- Plan *plan = (Plan *) lfirst(planlist_item);
+ Node *stmt = (Node *) lfirst(stmtlist_item);
/*
* If we got a cancel signal in prior command, quit
*/
CHECK_FOR_INTERRUPTS();
- if (query->commandType == CMD_UTILITY)
- {
- /*
- * process utility functions (create, destroy, etc..)
- */
- Assert(plan == NULL);
-
- PortalRunUtility(portal, query,
- query->canSetTag ? dest : altdest,
- completionTag);
- }
- else
+ if (IsA(stmt, PlannedStmt))
{
/*
* process a plannable query.
*/
+ PlannedStmt *pstmt = (PlannedStmt *) stmt;
+
if (log_executor_stats)
ResetUsage();
- if (query->canSetTag)
+ if (pstmt->canSetTag)
{
/* statement can set tag string */
- ProcessQuery(query, plan,
+ ProcessQuery(pstmt,
portal->portalParams,
dest, completionTag);
}
else
{
/* stmt added by rewrite cannot set tag */
- ProcessQuery(query, plan,
+ ProcessQuery(pstmt,
portal->portalParams,
altdest, NULL);
}
@@ -1162,12 +1251,25 @@ PortalRunMulti(Portal portal,
if (log_executor_stats)
ShowUsage("EXECUTOR STATISTICS");
}
+ else
+ {
+ /*
+ * process utility functions (create, destroy, etc..)
+ *
+ * These are assumed canSetTag if they're the only stmt in the
+ * portal.
+ */
+ if (list_length(portal->stmts) == 1)
+ PortalRunUtility(portal, stmt, dest, completionTag);
+ else
+ PortalRunUtility(portal, stmt, altdest, NULL);
+ }
/*
* Increment command counter between queries, but not after the last
* one.
*/
- if (lnext(planlist_item) != NULL)
+ if (lnext(stmtlist_item) != NULL)
CommandCounterIncrement();
/*
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 5cabec2970b..47051ad1ed2 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.272 2007/02/14 01:58:57 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.273 2007/02/20 17:32:16 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -248,36 +248,41 @@ CheckRelationOwnership(RangeVar *rel, bool noCatalogs)
/*
- * QueryIsReadOnly: is an analyzed/rewritten query read-only?
+ * CommandIsReadOnly: is an executable query read-only?
*
* This is a much stricter test than we apply for XactReadOnly mode;
* the query must be *in truth* read-only, because the caller wishes
* not to do CommandCounterIncrement for it.
+ *
+ * Note: currently no need to support Query nodes here
*/
bool
-QueryIsReadOnly(Query *parsetree)
+CommandIsReadOnly(Node *parsetree)
{
- switch (parsetree->commandType)
+ if (IsA(parsetree, PlannedStmt))
{
- case CMD_SELECT:
- if (parsetree->into != NULL)
- return false; /* SELECT INTO */
- else if (parsetree->rowMarks != NIL)
- return false; /* SELECT FOR UPDATE/SHARE */
- else
- return true;
- case CMD_UPDATE:
- case CMD_INSERT:
- case CMD_DELETE:
- return false;
- case CMD_UTILITY:
- /* For now, treat all utility commands as read/write */
- return false;
- default:
- elog(WARNING, "unrecognized commandType: %d",
- (int) parsetree->commandType);
- break;
+ PlannedStmt *stmt = (PlannedStmt *) parsetree;
+
+ switch (stmt->commandType)
+ {
+ case CMD_SELECT:
+ if (stmt->into != NULL)
+ return false; /* SELECT INTO */
+ else if (stmt->rowMarks != NIL)
+ return false; /* SELECT FOR UPDATE/SHARE */
+ else
+ return true;
+ case CMD_UPDATE:
+ case CMD_INSERT:
+ case CMD_DELETE:
+ return false;
+ default:
+ elog(WARNING, "unrecognized commandType: %d",
+ (int) stmt->commandType);
+ break;
+ }
}
+ /* For now, treat all utility commands as read/write */
return false;
}
@@ -1161,7 +1166,7 @@ UtilityReturnsTuples(Node *parsetree)
entry = FetchPreparedStatement(stmt->name, false);
if (!entry)
return false; /* not our business to raise error */
- switch (ChoosePortalStrategy(entry->query_list))
+ switch (ChoosePortalStrategy(entry->stmt_list))
{
case PORTAL_ONE_SELECT:
case PORTAL_ONE_RETURNING:
@@ -1244,6 +1249,7 @@ UtilityTupleDescriptor(Node *parsetree)
* QueryReturnsTuples
* Return "true" if this Query will send output to the destination.
*/
+#ifdef NOT_USED
bool
QueryReturnsTuples(Query *parsetree)
{
@@ -1270,14 +1276,15 @@ QueryReturnsTuples(Query *parsetree)
}
return false; /* default */
}
+#endif
/*
* CreateCommandTag
- * utility to get a string representation of the
- * command operation, given a raw (un-analyzed) parsetree.
+ * utility to get a string representation of the command operation,
+ * given either a raw (un-analyzed) parsetree or a planned query.
*
- * This must handle all raw command types, but since the vast majority
+ * This must handle all command types, but since the vast majority
* of 'em are utility commands, it seems sensible to keep it here.
*
* NB: all result strings must be shorter than COMPLETION_TAG_BUFSIZE.
@@ -1290,6 +1297,7 @@ CreateCommandTag(Node *parsetree)
switch (nodeTag(parsetree))
{
+ /* raw plannable queries */
case T_InsertStmt:
tag = "INSERT";
break;
@@ -1306,6 +1314,7 @@ CreateCommandTag(Node *parsetree)
tag = "SELECT";
break;
+ /* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
{
TransactionStmt *stmt = (TransactionStmt *) parsetree;
@@ -1826,65 +1835,98 @@ CreateCommandTag(Node *parsetree)
tag = "DEALLOCATE";
break;
- default:
- elog(WARNING, "unrecognized node type: %d",
- (int) nodeTag(parsetree));
- tag = "???";
- break;
- }
-
- return tag;
-}
-
-/*
- * CreateQueryTag
- * utility to get a string representation of a Query operation.
- *
- * This is exactly like CreateCommandTag, except it works on a Query
- * that has already been through parse analysis (and possibly further).
- */
-const char *
-CreateQueryTag(Query *parsetree)
-{
- const char *tag;
-
- Assert(IsA(parsetree, Query));
+ /* already-planned queries */
+ case T_PlannedStmt:
+ {
+ PlannedStmt *stmt = (PlannedStmt *) parsetree;
- switch (parsetree->commandType)
- {
- case CMD_SELECT:
+ switch (stmt->commandType)
+ {
+ case CMD_SELECT:
+ /*
+ * We take a little extra care here so that the result
+ * will be useful for complaints about read-only
+ * statements
+ */
+ if (stmt->into != NULL)
+ tag = "SELECT INTO";
+ else if (stmt->rowMarks != NIL)
+ {
+ if (((RowMarkClause *) linitial(stmt->rowMarks))->forUpdate)
+ tag = "SELECT FOR UPDATE";
+ else
+ tag = "SELECT FOR SHARE";
+ }
+ else
+ tag = "SELECT";
+ break;
+ case CMD_UPDATE:
+ tag = "UPDATE";
+ break;
+ case CMD_INSERT:
+ tag = "INSERT";
+ break;
+ case CMD_DELETE:
+ tag = "DELETE";
+ break;
+ default:
+ elog(WARNING, "unrecognized commandType: %d",
+ (int) stmt->commandType);
+ tag = "???";
+ break;
+ }
+ }
+ break;
- /*
- * We take a little extra care here so that the result will be
- * useful for complaints about read-only statements
- */
- if (parsetree->into != NULL)
- tag = "SELECT INTO";
- else if (parsetree->rowMarks != NIL)
+ /* parsed-and-rewritten-but-not-planned queries */
+ case T_Query:
{
- if (((RowMarkClause *) linitial(parsetree->rowMarks))->forUpdate)
- tag = "SELECT FOR UPDATE";
- else
- tag = "SELECT FOR SHARE";
+ Query *stmt = (Query *) parsetree;
+
+ switch (stmt->commandType)
+ {
+ case CMD_SELECT:
+ /*
+ * We take a little extra care here so that the result
+ * will be useful for complaints about read-only
+ * statements
+ */
+ if (stmt->into != NULL)
+ tag = "SELECT INTO";
+ else if (stmt->rowMarks != NIL)
+ {
+ if (((RowMarkClause *) linitial(stmt->rowMarks))->forUpdate)
+ tag = "SELECT FOR UPDATE";
+ else
+ tag = "SELECT FOR SHARE";
+ }
+ else
+ tag = "SELECT";
+ break;
+ case CMD_UPDATE:
+ tag = "UPDATE";
+ break;
+ case CMD_INSERT:
+ tag = "INSERT";
+ break;
+ case CMD_DELETE:
+ tag = "DELETE";
+ break;
+ case CMD_UTILITY:
+ tag = CreateCommandTag(stmt->utilityStmt);
+ break;
+ default:
+ elog(WARNING, "unrecognized commandType: %d",
+ (int) stmt->commandType);
+ tag = "???";
+ break;
+ }
}
- else
- tag = "SELECT";
- break;
- case CMD_UPDATE:
- tag = "UPDATE";
- break;
- case CMD_INSERT:
- tag = "INSERT";
- break;
- case CMD_DELETE:
- tag = "DELETE";
- break;
- case CMD_UTILITY:
- tag = CreateCommandTag(parsetree->utilityStmt);
break;
+
default:
- elog(WARNING, "unrecognized commandType: %d",
- (int) parsetree->commandType);
+ elog(WARNING, "unrecognized node type: %d",
+ (int) nodeTag(parsetree));
tag = "???";
break;
}
@@ -1896,9 +1938,9 @@ CreateQueryTag(Query *parsetree)
/*
* GetCommandLogLevel
* utility to get the minimum log_statement level for a command,
- * given a raw (un-analyzed) parsetree.
+ * given either a raw (un-analyzed) parsetree or a planned query.
*
- * This must handle all raw command types, but since the vast majority
+ * This must handle all command types, but since the vast majority
* of 'em are utility commands, it seems sensible to keep it here.
*/
LogStmtLevel
@@ -1908,6 +1950,7 @@ GetCommandLogLevel(Node *parsetree)
switch (nodeTag(parsetree))
{
+ /* raw plannable queries */
case T_InsertStmt:
case T_DeleteStmt:
case T_UpdateStmt:
@@ -1921,6 +1964,7 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_ALL;
break;
+ /* utility statements --- same whether raw or cooked */
case T_TransactionStmt:
lev = LOGSTMT_ALL;
break;
@@ -2216,12 +2260,12 @@ GetCommandLogLevel(Node *parsetree)
pstmt = FetchPreparedStatement(stmt->name, false);
if (pstmt)
{
- foreach(l, pstmt->query_list)
+ foreach(l, pstmt->stmt_list)
{
- Query *query = (Query *) lfirst(l);
+ Node *substmt = (Node *) lfirst(l);
LogStmtLevel stmt_lev;
- stmt_lev = GetQueryLogLevel(query);
+ stmt_lev = GetCommandLogLevel(substmt);
lev = Min(lev, stmt_lev);
}
}
@@ -2232,62 +2276,72 @@ GetCommandLogLevel(Node *parsetree)
lev = LOGSTMT_ALL;
break;
- case T_Query:
+ /* already-planned queries */
+ case T_PlannedStmt:
+ {
+ PlannedStmt *stmt = (PlannedStmt *) parsetree;
- /*
- * In complicated situations (eg, EXPLAIN ANALYZE in an extended
- * Query protocol), we might find an already-analyzed query within
- * a utility statement. Cope.
- */
- lev = GetQueryLogLevel((Query *) parsetree);
- break;
+ switch (stmt->commandType)
+ {
+ case CMD_SELECT:
+ if (stmt->into != NULL)
+ lev = LOGSTMT_DDL; /* CREATE AS, SELECT INTO */
+ else
+ lev = LOGSTMT_ALL;
+ break;
- default:
- elog(WARNING, "unrecognized node type: %d",
- (int) nodeTag(parsetree));
- lev = LOGSTMT_ALL;
+ case CMD_UPDATE:
+ case CMD_INSERT:
+ case CMD_DELETE:
+ lev = LOGSTMT_MOD;
+ break;
+
+ default:
+ elog(WARNING, "unrecognized commandType: %d",
+ (int) stmt->commandType);
+ lev = LOGSTMT_ALL;
+ break;
+ }
+ }
break;
- }
- return lev;
-}
+ /* parsed-and-rewritten-but-not-planned queries */
+ case T_Query:
+ {
+ Query *stmt = (Query *) parsetree;
-/*
- * GetQueryLogLevel
- * utility to get the minimum log_statement level for a Query operation.
- *
- * This is exactly like GetCommandLogLevel, except it works on a Query
- * that has already been through parse analysis (and possibly further).
- */
-LogStmtLevel
-GetQueryLogLevel(Query *parsetree)
-{
- LogStmtLevel lev;
+ switch (stmt->commandType)
+ {
+ case CMD_SELECT:
+ if (stmt->into != NULL)
+ lev = LOGSTMT_DDL; /* CREATE AS, SELECT INTO */
+ else
+ lev = LOGSTMT_ALL;
+ break;
- Assert(IsA(parsetree, Query));
+ case CMD_UPDATE:
+ case CMD_INSERT:
+ case CMD_DELETE:
+ lev = LOGSTMT_MOD;
+ break;
- switch (parsetree->commandType)
- {
- case CMD_SELECT:
- if (parsetree->into != NULL)
- lev = LOGSTMT_DDL; /* CREATE AS, SELECT INTO */
- else
- lev = LOGSTMT_ALL;
- break;
+ case CMD_UTILITY:
+ lev = GetCommandLogLevel(stmt->utilityStmt);
+ break;
- case CMD_UPDATE:
- case CMD_INSERT:
- case CMD_DELETE:
- lev = LOGSTMT_MOD;
- break;
+ default:
+ elog(WARNING, "unrecognized commandType: %d",
+ (int) stmt->commandType);
+ lev = LOGSTMT_ALL;
+ break;
+ }
- case CMD_UTILITY:
- lev = GetCommandLogLevel(parsetree->utilityStmt);
+ }
break;
default:
- elog(WARNING, "unrecognized commandType: %d",
- (int) parsetree->commandType);
+ elog(WARNING, "unrecognized node type: %d",
+ (int) nodeTag(parsetree));
lev = LOGSTMT_ALL;
break;
}