diff options
Diffstat (limited to 'src/backend/tcop')
-rw-r--r-- | src/backend/tcop/postgres.c | 150 | ||||
-rw-r--r-- | src/backend/tcop/pquery.c | 338 | ||||
-rw-r--r-- | src/backend/tcop/utility.c | 310 |
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; } |