📄 prepare.c
字号:
/*------------------------------------------------------------------------- * * prepare.c * Prepareable SQL statements via PREPARE, EXECUTE and DEALLOCATE * * This module also implements storage of prepared statements that are * accessed via the extended FE/BE query protocol. * * * Copyright (c) 2002-2005, PostgreSQL Global Development Group * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.42.2.1 2005/12/14 17:06:37 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "commands/explain.h"#include "commands/prepare.h"#include "executor/executor.h"#include "utils/guc.h"#include "optimizer/planner.h"#include "rewrite/rewriteHandler.h"#include "tcop/pquery.h"#include "tcop/tcopprot.h"#include "tcop/utility.h"#include "utils/hsearch.h"#include "utils/memutils.h"/* * The hash table in which prepared queries are stored. This is * per-backend: query plans are not shared between backends. * The keys for this hash table are the arguments to PREPARE and EXECUTE * (statement names); the entries are PreparedStatement structs. */static HTAB *prepared_queries = NULL;static void InitQueryHashTable(void);static ParamListInfo EvaluateParams(EState *estate, List *params, List *argtypes);/* * Implements the 'PREPARE' utility statement. */voidPrepareQuery(PrepareStmt *stmt){ const char *commandTag; Query *query; List *query_list, *plan_list; /* * Disallow empty-string statement name (conflicts with protocol-level * unnamed statement). */ if (!stmt->name || stmt->name[0] == '\0') ereport(ERROR, (errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION), errmsg("invalid statement name: must not be empty"))); switch (stmt->query->commandType) { case CMD_SELECT: commandTag = "SELECT"; break; case CMD_INSERT: commandTag = "INSERT"; break; case CMD_UPDATE: commandTag = "UPDATE"; break; case CMD_DELETE: commandTag = "DELETE"; break; default: ereport(ERROR, (errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION), errmsg("utility statements cannot be prepared"))); commandTag = NULL; /* keep compiler quiet */ break; } /* * Parse analysis is already done, but we must still rewrite and plan the * query. */ /* * Because the planner is not cool about not scribbling on its input, we * make a preliminary copy of the source querytree. This prevents * problems in the case that the PREPARE is in a portal or plpgsql * function and is executed repeatedly. (See also the same hack in * DECLARE CURSOR and EXPLAIN.) XXX the planner really shouldn't modify * its input ... FIXME someday. */ query = copyObject(stmt->query); /* Rewrite the query. The result could be 0, 1, or many queries. */ AcquireRewriteLocks(query); query_list = QueryRewrite(query); /* Generate plans for queries. Snapshot is already set. */ plan_list = pg_plan_queries(query_list, NULL, false); /* * Save the results. We don't have the query string for this PREPARE, but * we do have the string we got from the client, so use that. */ StorePreparedStatement(stmt->name, debug_query_string, commandTag, query_list, plan_list, stmt->argtype_oids);}/* * Implements the 'EXECUTE' utility statement. */voidExecuteQuery(ExecuteStmt *stmt, DestReceiver *dest, char *completionTag){ PreparedStatement *entry; char *query_string; List *query_list, *plan_list; MemoryContext qcontext; ParamListInfo paramLI = NULL; EState *estate = NULL; Portal portal; /* Look it up in the hash table */ entry = FetchPreparedStatement(stmt->name, true); query_string = entry->query_string; query_list = entry->query_list; plan_list = entry->plan_list; qcontext = entry->context; 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, stmt->params, entry->argtype_list); } /* * Create a new portal to run the query in */ portal = CreateNewPortal(); /* * For CREATE TABLE / AS EXECUTE, make a copy of the stored query so that * we can modify its destination (yech, but this has always been ugly). * For regular EXECUTE we can just use the stored query where it sits, * since the executor is read-only. */ if (stmt->into) { MemoryContext oldContext; Query *query; oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); if (query_string) query_string = pstrdup(query_string); query_list = copyObject(query_list); plan_list = copyObject(plan_list); qcontext = PortalGetHeapMemory(portal); if (list_length(query_list) != 1) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("prepared statement is not a SELECT"))); query = (Query *) linitial(query_list); if (query->commandType != CMD_SELECT) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("prepared statement is not a SELECT"))); query->into = copyObject(stmt->into); MemoryContextSwitchTo(oldContext); } PortalDefineQuery(portal, query_string, entry->commandTag, query_list, plan_list, qcontext); /* * Run the portal to completion. */ PortalStart(portal, paramLI, ActiveSnapshot); (void) PortalRun(portal, FETCH_ALL, dest, dest, completionTag); PortalDrop(portal, false); if (estate) FreeExecutorState(estate); /* No need to pfree other memory, MemoryContext will be reset */}/* * Evaluates a list of parameters, using the given executor state. It * requires a list of the parameter expressions themselves, and a list of * their types. It returns a filled-in ParamListInfo -- this can later * be passed to CreateQueryDesc(), which allows the executor to make use * of the parameters during query execution. */static ParamListInfoEvaluateParams(EState *estate, List *params, List *argtypes){ int nargs = list_length(argtypes); ParamListInfo paramLI; List *exprstates; ListCell *le, *la; int i = 0; /* Parser should have caught this error, but check for safety */ if (list_length(params) != nargs) elog(ERROR, "wrong number of arguments"); exprstates = (List *) ExecPrepareExpr((Expr *) params, estate); paramLI = (ParamListInfo) palloc0((nargs + 1) * sizeof(ParamListInfoData)); forboth(le, exprstates, la, argtypes) { ExprState *n = lfirst(le); bool isNull; paramLI[i].value = ExecEvalExprSwitchContext(n, GetPerTupleExprContext(estate), &isNull, NULL); paramLI[i].kind = PARAM_NUM; paramLI[i].id = i + 1; paramLI[i].ptype = lfirst_oid(la); paramLI[i].isnull = isNull; i++; } paramLI[i].kind = PARAM_INVALID; return paramLI;}/* * Initialize query hash table upon first use. */static voidInitQueryHashTable(void){ HASHCTL hash_ctl; MemSet(&hash_ctl, 0, sizeof(hash_ctl)); hash_ctl.keysize = NAMEDATALEN; hash_ctl.entrysize = sizeof(PreparedStatement); prepared_queries = hash_create("Prepared Queries", 32, &hash_ctl, HASH_ELEM);}/* * Store all the data pertaining to a query in the hash table using * the specified key. A copy of the data is made in a memory context belonging * to the hash entry, so the caller can dispose of their copy. * * Exception: commandTag is presumed to be a pointer to a constant string, * or possibly NULL, so it need not be copied. Note that commandTag should * be NULL only if the original query (before rewriting) was empty. */voidStorePreparedStatement(const char *stmt_name, const char *query_string, const char *commandTag, List *query_list, List *plan_list, List *argtype_list){ PreparedStatement *entry; MemoryContext oldcxt, entrycxt; char *qstring; char key[NAMEDATALEN]; bool found; /* Initialize the hash table, if necessary */ if (!prepared_queries) InitQueryHashTable(); /* Check for pre-existing entry of same name */ /* See notes in FetchPreparedStatement */ StrNCpy(key, stmt_name, sizeof(key)); hash_search(prepared_queries, key, HASH_FIND, &found); if (found) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_PSTATEMENT), errmsg("prepared statement \"%s\" already exists", stmt_name))); /* Make a permanent memory context for the hashtable entry */ entrycxt = AllocSetContextCreate(TopMemoryContext, stmt_name, ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MAXSIZE);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -