]> granicus.if.org Git - postgresql/blob - src/backend/commands/prepare.c
> I'm not sure why NDirectFileRead/NDirectFileWrite are unused at the
[postgresql] / src / backend / commands / prepare.c
1 /*-------------------------------------------------------------------------
2  *
3  * prepare.c
4  *        Prepareable SQL statements via PREPARE, EXECUTE and DEALLOCATE
5  *
6  * Copyright (c) 2002, PostgreSQL Global Development Group
7  *
8  * IDENTIFICATION
9  *        $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.4 2002/09/20 03:45:08 momjian Exp $
10  *
11  *-------------------------------------------------------------------------
12  */
13 #include "postgres.h"
14
15 #include "commands/prepare.h"
16 #include "executor/executor.h"
17 #include "utils/guc.h"
18 #include "optimizer/planner.h"
19 #include "rewrite/rewriteHandler.h"
20 #include "tcop/pquery.h"
21 #include "tcop/tcopprot.h"
22 #include "tcop/utility.h"
23 #include "utils/hsearch.h"
24 #include "utils/memutils.h"
25
26
27 #define HASH_KEY_LEN NAMEDATALEN
28
29 /* All the data we need to remember about a stored query */
30 typedef struct
31 {
32         /* dynahash.c requires key to be first field */
33         char            key[HASH_KEY_LEN];
34         List       *query_list;         /* list of queries */
35         List       *plan_list;          /* list of plans */
36         List       *argtype_list;       /* list of parameter type OIDs */
37         MemoryContext context;          /* context containing this query */
38 } QueryHashEntry;
39
40 /*
41  * The hash table in which prepared queries are stored. This is
42  * per-backend: query plans are not shared between backends.
43  * The keys for this hash table are the arguments to PREPARE
44  * and EXECUTE ("plan names"); the entries are QueryHashEntry structs.
45  */
46 static HTAB *prepared_queries = NULL;
47
48 static void InitQueryHashTable(void);
49 static void StoreQuery(const char *stmt_name, List *query_list,
50                    List *plan_list, List *argtype_list);
51 static QueryHashEntry *FetchQuery(const char *plan_name);
52 static void RunQuery(QueryDesc *qdesc, EState *state);
53
54
55 /*
56  * Implements the 'PREPARE' utility statement.
57  */
58 void
59 PrepareQuery(PrepareStmt *stmt)
60 {
61         List       *plan_list = NIL;
62         List       *query_list,
63                            *query_list_item;
64
65         if (!stmt->name)
66                 elog(ERROR, "No statement name given");
67
68         if (stmt->query->commandType == CMD_UTILITY)
69                 elog(ERROR, "Utility statements cannot be prepared");
70
71         /* Rewrite the query. The result could be 0, 1, or many queries. */
72         query_list = QueryRewrite(stmt->query);
73
74         foreach(query_list_item, query_list)
75         {
76                 Query      *query = (Query *) lfirst(query_list_item);
77                 Plan       *plan;
78
79                 plan = pg_plan_query(query);
80
81                 plan_list = lappend(plan_list, plan);
82         }
83
84         StoreQuery(stmt->name, query_list, plan_list, stmt->argtype_oids);
85 }
86
87 /*
88  * Implements the 'EXECUTE' utility statement.
89  */
90 void
91 ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
92 {
93         QueryHashEntry *entry;
94         List       *l,
95                            *query_list,
96                            *plan_list;
97         ParamListInfo paramLI = NULL;
98
99         /* Look it up in the hash table */
100         entry = FetchQuery(stmt->name);
101
102         /* Make working copies the executor can safely scribble on */
103         query_list = (List *) copyObject(entry->query_list);
104         plan_list = (List *) copyObject(entry->plan_list);
105
106         Assert(length(query_list) == length(plan_list));
107
108         /* Evaluate parameters, if any */
109         if (entry->argtype_list != NIL)
110         {
111                 int                     nargs = length(entry->argtype_list);
112                 int                     i = 0;
113                 ExprContext *econtext = MakeExprContext(NULL, CurrentMemoryContext);
114
115                 /* Parser should have caught this error, but check */
116                 if (nargs != length(stmt->params))
117                         elog(ERROR, "ExecuteQuery: wrong number of arguments");
118
119                 paramLI = (ParamListInfo) palloc((nargs + 1) * sizeof(ParamListInfoData));
120                 MemSet(paramLI, 0, (nargs + 1) * sizeof(ParamListInfoData));
121
122                 foreach(l, stmt->params)
123                 {
124                         Node       *n = lfirst(l);
125                         bool            isNull;
126
127                         paramLI[i].value = ExecEvalExprSwitchContext(n,
128                                                                                                                  econtext,
129                                                                                                                  &isNull,
130                                                                                                                  NULL);
131                         paramLI[i].kind = PARAM_NUM;
132                         paramLI[i].id = i + 1;
133                         paramLI[i].isnull = isNull;
134
135                         i++;
136                 }
137                 paramLI[i].kind = PARAM_INVALID;
138         }
139
140         /* Execute each query */
141         foreach(l, query_list)
142         {
143                 Query      *query = lfirst(l);
144                 Plan       *plan = lfirst(plan_list);
145                 bool            is_last_query;
146
147                 plan_list = lnext(plan_list);
148                 is_last_query = (plan_list == NIL);
149
150                 if (query->commandType == CMD_UTILITY)
151                         ProcessUtility(query->utilityStmt, outputDest, NULL);
152                 else
153                 {
154                         QueryDesc  *qdesc;
155                         EState     *state;
156
157                         if (Show_executor_stats)
158                                 ResetUsage();
159
160                         qdesc = CreateQueryDesc(query, plan, outputDest, NULL);
161                         state = CreateExecutorState();
162
163                         state->es_param_list_info = paramLI;
164
165                         if (stmt->into)
166                         {
167                                 if (qdesc->operation != CMD_SELECT)
168                                         elog(ERROR, "INTO clause specified for non-SELECT query");
169
170                                 query->into = stmt->into;
171                                 qdesc->dest = None;
172                         }
173
174                         RunQuery(qdesc, state);
175
176                         if (Show_executor_stats)
177                                 ShowUsage("EXECUTOR STATISTICS");
178                 }
179
180                 /*
181                  * If we're processing multiple queries, we need to increment the
182                  * command counter between them. For the last query, there's no
183                  * need to do this, it's done automatically.
184                  */
185                 if (!is_last_query)
186                         CommandCounterIncrement();
187         }
188
189         /* No need to pfree memory, MemoryContext will be reset */
190 }
191
192 /*
193  * Initialize query hash table upon first use.
194  */
195 static void
196 InitQueryHashTable(void)
197 {
198         HASHCTL         hash_ctl;
199
200         MemSet(&hash_ctl, 0, sizeof(hash_ctl));
201
202         hash_ctl.keysize = HASH_KEY_LEN;
203         hash_ctl.entrysize = sizeof(QueryHashEntry);
204
205         prepared_queries = hash_create("Prepared Queries",
206                                                                    32,
207                                                                    &hash_ctl,
208                                                                    HASH_ELEM);
209
210         if (!prepared_queries)
211                 elog(ERROR, "InitQueryHashTable: unable to create hash table");
212 }
213
214 /*
215  * Store all the data pertaining to a query in the hash table using
216  * the specified key. A copy of the data is made in a memory context belonging
217  * to the hash entry, so the caller can dispose of their copy.
218  */
219 static void
220 StoreQuery(const char *stmt_name, List *query_list, List *plan_list,
221                    List *argtype_list)
222 {
223         QueryHashEntry *entry;
224         MemoryContext oldcxt,
225                                 entrycxt;
226         char            key[HASH_KEY_LEN];
227         bool            found;
228
229         /* Initialize the hash table, if necessary */
230         if (!prepared_queries)
231                 InitQueryHashTable();
232
233         /* Check for pre-existing entry of same name */
234         /* See notes in FetchQuery */
235         MemSet(key, 0, sizeof(key));
236         strncpy(key, stmt_name, sizeof(key));
237
238         hash_search(prepared_queries, key, HASH_FIND, &found);
239
240         if (found)
241                 elog(ERROR, "Prepared statement with name \"%s\" already exists",
242                          stmt_name);
243
244         /* Make a permanent memory context for the hashtable entry */
245         entrycxt = AllocSetContextCreate(TopMemoryContext,
246                                                                          stmt_name,
247                                                                          1024,
248                                                                          1024,
249                                                                          ALLOCSET_DEFAULT_MAXSIZE);
250
251         oldcxt = MemoryContextSwitchTo(entrycxt);
252
253         /*
254          * We need to copy the data so that it is stored in the correct memory
255          * context.  Do this before making hashtable entry, so that an
256          * out-of-memory failure only wastes memory and doesn't leave us with
257          * an incomplete (ie corrupt) hashtable entry.
258          */
259         query_list = (List *) copyObject(query_list);
260         plan_list = (List *) copyObject(plan_list);
261         argtype_list = listCopy(argtype_list);
262
263         /* Now we can add entry to hash table */
264         entry = (QueryHashEntry *) hash_search(prepared_queries,
265                                                                                    key,
266                                                                                    HASH_ENTER,
267                                                                                    &found);
268
269         /* Shouldn't get a failure, nor duplicate entry */
270         if (!entry || found)
271                 elog(ERROR, "Unable to store prepared statement \"%s\"!",
272                          stmt_name);
273
274         /* Fill in the hash table entry with copied data */
275         entry->query_list = query_list;
276         entry->plan_list = plan_list;
277         entry->argtype_list = argtype_list;
278         entry->context = entrycxt;
279
280         MemoryContextSwitchTo(oldcxt);
281 }
282
283 /*
284  * Lookup an existing query in the hash table.
285  */
286 static QueryHashEntry *
287 FetchQuery(const char *plan_name)
288 {
289         char            key[HASH_KEY_LEN];
290         QueryHashEntry *entry;
291
292         /*
293          * If the hash table hasn't been initialized, it can't be storing
294          * anything, therefore it couldn't possibly store our plan.
295          */
296         if (!prepared_queries)
297                 elog(ERROR, "Prepared statement with name \"%s\" does not exist",
298                          plan_name);
299
300         /*
301          * We can't just use the statement name as supplied by the user: the
302          * hash package is picky enough that it needs to be NULL-padded out to
303          * the appropriate length to work correctly.
304          */
305         MemSet(key, 0, sizeof(key));
306         strncpy(key, plan_name, sizeof(key));
307
308         entry = (QueryHashEntry *) hash_search(prepared_queries,
309                                                                                    key,
310                                                                                    HASH_FIND,
311                                                                                    NULL);
312
313         if (!entry)
314                 elog(ERROR, "Prepared statement with name \"%s\" does not exist",
315                          plan_name);
316
317         return entry;
318 }
319
320 /*
321  * Given a plan name, look up the stored plan (giving error if not found).
322  * If found, return the list of argument type OIDs.
323  */
324 List *
325 FetchQueryParams(const char *plan_name)
326 {
327         QueryHashEntry *entry;
328
329         entry = FetchQuery(plan_name);
330
331         return entry->argtype_list;
332 }
333
334 /*
335  * Actually execute a prepared query.
336  */
337 static void
338 RunQuery(QueryDesc *qdesc, EState *state)
339 {
340         TupleDesc       tupdesc;
341
342         tupdesc = ExecutorStart(qdesc, state);
343
344         ExecutorRun(qdesc, state, state->es_direction, 0L);
345
346         ExecutorEnd(qdesc, state);
347 }
348
349 /*
350  * Implements the 'DEALLOCATE' utility statement: deletes the
351  * specified plan from storage.
352  *
353  * The initial part of this routine is identical to FetchQuery(),
354  * but we repeat the coding because we need to use the key twice.
355  */
356 void
357 DeallocateQuery(DeallocateStmt *stmt)
358 {
359         char            key[HASH_KEY_LEN];
360         QueryHashEntry *entry;
361
362         /*
363          * If the hash table hasn't been initialized, it can't be storing
364          * anything, therefore it couldn't possibly store our plan.
365          */
366         if (!prepared_queries)
367                 elog(ERROR, "Prepared statement with name \"%s\" does not exist",
368                          stmt->name);
369
370         /*
371          * We can't just use the statement name as supplied by the user: the
372          * hash package is picky enough that it needs to be NULL-padded out to
373          * the appropriate length to work correctly.
374          */
375         MemSet(key, 0, sizeof(key));
376         strncpy(key, stmt->name, sizeof(key));
377
378         /*
379          * First lookup the entry, so we can release all the subsidiary memory
380          * it has allocated (when it's removed, hash_search() will return a
381          * dangling pointer, so it needs to be done prior to HASH_REMOVE).
382          * This requires an extra hash-table lookup, but DEALLOCATE isn't
383          * exactly a performance bottleneck.
384          */
385         entry = (QueryHashEntry *) hash_search(prepared_queries,
386                                                                                    key,
387                                                                                    HASH_FIND,
388                                                                                    NULL);
389
390         if (!entry)
391                 elog(ERROR, "Prepared statement with name \"%s\" does not exist",
392                          stmt->name);
393
394         /* Flush the context holding the subsidiary data */
395         Assert(MemoryContextIsValid(entry->context));
396         MemoryContextDelete(entry->context);
397
398         /* Now we can remove the hash table entry */
399         hash_search(prepared_queries, key, HASH_REMOVE, NULL);
400 }