📄 prepare.c
字号:
oldcxt = MemoryContextSwitchTo(entrycxt); /* * We need to copy the data so that it is stored in the correct memory * context. Do this before making hashtable entry, so that an * out-of-memory failure only wastes memory and doesn't leave us with an * incomplete (ie corrupt) hashtable entry. */ qstring = query_string ? pstrdup(query_string) : NULL; query_list = (List *) copyObject(query_list); plan_list = (List *) copyObject(plan_list); argtype_list = list_copy(argtype_list); /* Now we can add entry to hash table */ entry = (PreparedStatement *) hash_search(prepared_queries, key, HASH_ENTER, &found); /* Shouldn't get a duplicate entry */ if (found) elog(ERROR, "duplicate prepared statement \"%s\"", 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->argtype_list = argtype_list; entry->context = entrycxt; MemoryContextSwitchTo(oldcxt);}/* * Lookup an existing query in the hash table. If the query does not * actually exist, throw ereport(ERROR) or return NULL per second parameter. */PreparedStatement *FetchPreparedStatement(const char *stmt_name, bool throwError){ char key[NAMEDATALEN]; PreparedStatement *entry; /* * If the hash table hasn't been initialized, it can't be storing * anything, therefore it couldn't possibly store our plan. */ if (prepared_queries) { /* * We can't just use the statement name as supplied by the user: the * hash package is picky enough that it needs to be NULL-padded out to * the appropriate length to work correctly. */ StrNCpy(key, stmt_name, sizeof(key)); entry = (PreparedStatement *) hash_search(prepared_queries, key, HASH_FIND, NULL); } else entry = NULL; if (!entry && throwError) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_PSTATEMENT), errmsg("prepared statement \"%s\" does not exist", stmt_name))); return entry;}/* * Look up a prepared statement given the name (giving error if not found). * If found, return the list of argument type OIDs. */List *FetchPreparedStatementParams(const char *stmt_name){ PreparedStatement *entry; entry = FetchPreparedStatement(stmt_name, true); return entry->argtype_list;}/* * Given a prepared statement, determine the result tupledesc it will * produce. Returns NULL if the execution will not return tuples. * * Note: the result is created or copied into current memory context. */TupleDescFetchPreparedStatementResultDesc(PreparedStatement *stmt){ Query *query; switch (ChoosePortalStrategy(stmt->query_list)) { case PORTAL_ONE_SELECT: query = (Query *) linitial(stmt->query_list); return ExecCleanTypeFromTL(query->targetList, false); case PORTAL_UTIL_SELECT: query = (Query *) linitial(stmt->query_list); return UtilityTupleDescriptor(query->utilityStmt); case PORTAL_MULTI_QUERY: /* will not return tuples */ break; } return NULL;}/* * Given a prepared statement, determine whether it will return tuples. * * Note: this is used rather than just testing the result of * FetchPreparedStatementResultDesc() because that routine can fail if * invoked in an aborted transaction. This one is safe to use in any * context. Be sure to keep the two routines in sync! */boolPreparedStatementReturnsTuples(PreparedStatement *stmt){ switch (ChoosePortalStrategy(stmt->query_list)) { case PORTAL_ONE_SELECT: case PORTAL_UTIL_SELECT: return true; case PORTAL_MULTI_QUERY: /* will not return tuples */ break; } return false;}/* * Given a prepared statement that returns tuples, extract the query * targetlist. Returns NIL if the statement doesn't have a determinable * 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_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;}/* * Implements the 'DEALLOCATE' utility statement: deletes the * specified plan from storage. */voidDeallocateQuery(DeallocateStmt *stmt){ DropPreparedStatement(stmt->name, true);}/* * Internal version of DEALLOCATE * * If showError is false, dropping a nonexistent statement is a no-op. */voidDropPreparedStatement(const char *stmt_name, bool showError){ PreparedStatement *entry; /* Find the query's hash table entry; raise error if wanted */ entry = FetchPreparedStatement(stmt_name, showError); if (entry) { /* Drop any open portals that depend on this prepared statement */ Assert(MemoryContextIsValid(entry->context)); DropDependentPortals(entry->context); /* Flush the context holding the subsidiary data */ MemoryContextDelete(entry->context); /* Now we can remove the hash table entry */ hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL); }}/* * Implements the 'EXPLAIN EXECUTE' utility statement. */voidExplainExecuteQuery(ExplainStmt *stmt, TupOutputState *tstate){ ExecuteStmt *execstmt = (ExecuteStmt *) stmt->query->utilityStmt; PreparedStatement *entry; ListCell *q, *p; List *query_list, *plan_list; ParamListInfo paramLI = NULL; EState *estate = NULL; /* explain.c should only call me for EXECUTE stmt */ Assert(execstmt && IsA(execstmt, ExecuteStmt)); /* Look it up in the hash table */ entry = FetchPreparedStatement(execstmt->name, true); query_list = entry->query_list; plan_list = entry->plan_list; Assert(list_length(query_list) == list_length(plan_list)); /* Evaluate parameters, if any */ if (entry->argtype_list != NIL) { /* * Need an EState to evaluate parameters; must not delete it till end * of query, in case parameters are pass-by-reference. */ estate = CreateExecutorState(); paramLI = EvaluateParams(estate, execstmt->params, entry->argtype_list); } /* Explain each query */ forboth(q, query_list, p, plan_list) { Query *query = (Query *) lfirst(q); Plan *plan = (Plan *) lfirst(p); bool is_last_query; is_last_query = (lnext(p) == NULL); if (query->commandType == CMD_UTILITY) { if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt)) do_text_output_oneline(tstate, "NOTIFY"); else do_text_output_oneline(tstate, "UTILITY"); } else { QueryDesc *qdesc; if (execstmt->into) { if (query->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); query->into = execstmt->into; } /* * Update snapshot command ID to ensure this query sees results of * any previously executed queries. (It's a bit cheesy to modify * ActiveSnapshot without making a copy, but for the limited ways * in which EXPLAIN can be invoked, I think it's OK, because the * active snapshot shouldn't be shared with anything else anyway.) */ ActiveSnapshot->curcid = GetCurrentCommandId(); /* Create a QueryDesc requesting no output */ qdesc = CreateQueryDesc(query, plan, ActiveSnapshot, InvalidSnapshot, None_Receiver, paramLI, stmt->analyze); ExplainOnePlan(qdesc, stmt, tstate); } /* No need for CommandCounterIncrement, as ExplainOnePlan did it */ /* put a blank line between plans */ if (!is_last_query) do_text_output_oneline(tstate, ""); } if (estate) FreeExecutorState(estate);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -