]> granicus.if.org Git - postgresql/blob - src/backend/commands/prepare.c
Implement EXPLAIN EXECUTE. By Neil Conway, with some kibitzing from
[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.13 2003/02/02 23:46:38 tgl Exp $
10  *
11  *-------------------------------------------------------------------------
12  */
13 #include "postgres.h"
14
15 #include "commands/explain.h"
16 #include "commands/prepare.h"
17 #include "executor/executor.h"
18 #include "utils/guc.h"
19 #include "optimizer/planner.h"
20 #include "rewrite/rewriteHandler.h"
21 #include "tcop/pquery.h"
22 #include "tcop/tcopprot.h"
23 #include "tcop/utility.h"
24 #include "utils/hsearch.h"
25 #include "utils/memutils.h"
26
27
28 #define HASH_KEY_LEN NAMEDATALEN
29
30 /* All the data we need to remember about a stored query */
31 typedef struct
32 {
33         /* dynahash.c requires key to be first field */
34         char            key[HASH_KEY_LEN];
35         List       *query_list;         /* list of queries */
36         List       *plan_list;          /* list of plans */
37         List       *argtype_list;       /* list of parameter type OIDs */
38         MemoryContext context;          /* context containing this query */
39 } QueryHashEntry;
40
41 /*
42  * The hash table in which prepared queries are stored. This is
43  * per-backend: query plans are not shared between backends.
44  * The keys for this hash table are the arguments to PREPARE
45  * and EXECUTE ("plan names"); the entries are QueryHashEntry structs.
46  */
47 static HTAB *prepared_queries = NULL;
48
49 static void InitQueryHashTable(void);
50 static void StoreQuery(const char *stmt_name, List *query_list,
51                                            List *plan_list, List *argtype_list);
52 static QueryHashEntry *FetchQuery(const char *plan_name);
53 static ParamListInfo EvaluateParams(EState *estate,
54                                                                         List *params, List *argtypes);
55
56 /*
57  * Implements the 'PREPARE' utility statement.
58  */
59 void
60 PrepareQuery(PrepareStmt *stmt)
61 {
62         List       *plan_list = NIL;
63         List       *query_list,
64                            *query_list_item;
65
66         if (!stmt->name)
67                 elog(ERROR, "No statement name given");
68
69         if (stmt->query->commandType == CMD_UTILITY)
70                 elog(ERROR, "Utility statements cannot be prepared");
71
72         /* Rewrite the query. The result could be 0, 1, or many queries. */
73         query_list = QueryRewrite(stmt->query);
74
75         foreach(query_list_item, query_list)
76         {
77                 Query      *query = (Query *) lfirst(query_list_item);
78                 Plan       *plan;
79
80                 plan = pg_plan_query(query);
81
82                 plan_list = lappend(plan_list, plan);
83         }
84
85         StoreQuery(stmt->name, query_list, plan_list, stmt->argtype_oids);
86 }
87
88 /*
89  * Implements the 'EXECUTE' utility statement.
90  */
91 void
92 ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
93 {
94         QueryHashEntry *entry;
95         List       *l,
96                            *query_list,
97                            *plan_list;
98         ParamListInfo paramLI = NULL;
99         EState     *estate = NULL;
100
101         /* Look it up in the hash table */
102         entry = FetchQuery(stmt->name);
103
104         query_list = entry->query_list;
105         plan_list = entry->plan_list;
106
107         Assert(length(query_list) == length(plan_list));
108
109         /* Evaluate parameters, if any */
110         if (entry->argtype_list != NIL)
111         {
112                 /*
113                  * Need an EState to evaluate parameters; must not delete it
114                  * till end of query, in case parameters are pass-by-reference.
115                  */
116                 estate = CreateExecutorState();
117                 paramLI = EvaluateParams(estate, stmt->params, entry->argtype_list);
118         }
119
120         /* Execute each query */
121         foreach(l, query_list)
122         {
123                 Query     *query = (Query *) lfirst(l);
124                 Plan      *plan = (Plan *) lfirst(plan_list);
125                 bool            is_last_query;
126
127                 plan_list = lnext(plan_list);
128                 is_last_query = (plan_list == NIL);
129
130                 if (query->commandType == CMD_UTILITY)
131                         ProcessUtility(query->utilityStmt, outputDest, NULL);
132                 else
133                 {
134                         QueryDesc  *qdesc;
135
136                         if (log_executor_stats)
137                                 ResetUsage();
138
139                         qdesc = CreateQueryDesc(query, plan, outputDest, NULL,
140                                                                         paramLI, false);
141
142                         if (stmt->into)
143                         {
144                                 if (qdesc->operation != CMD_SELECT)
145                                         elog(ERROR, "INTO clause specified for non-SELECT query");
146
147                                 query->into = stmt->into;
148                                 qdesc->dest = None;
149                         }
150
151                         ExecutorStart(qdesc);
152
153                         ExecutorRun(qdesc, ForwardScanDirection, 0L);
154
155                         ExecutorEnd(qdesc);
156
157                         FreeQueryDesc(qdesc);
158
159                         if (log_executor_stats)
160                                 ShowUsage("EXECUTOR STATISTICS");
161                 }
162
163                 /*
164                  * If we're processing multiple queries, we need to increment the
165                  * command counter between them. For the last query, there's no
166                  * need to do this, it's done automatically.
167                  */
168                 if (!is_last_query)
169                         CommandCounterIncrement();
170         }
171
172         if (estate)
173                 FreeExecutorState(estate);
174
175         /* No need to pfree other memory, MemoryContext will be reset */
176 }
177
178 /*
179  * Evaluates a list of parameters, using the given executor state. It
180  * requires a list of the parameter values themselves, and a list of
181  * their types. It returns a filled-in ParamListInfo -- this can later
182  * be passed to CreateQueryDesc(), which allows the executor to make use
183  * of the parameters during query execution.
184  */
185 static ParamListInfo
186 EvaluateParams(EState *estate, List *params, List *argtypes)
187 {
188         int                             nargs = length(argtypes);
189         ParamListInfo   paramLI;
190         List               *exprstates;
191         List               *l;
192         int                             i = 0;
193
194         /* Parser should have caught this error, but check anyway */
195         if (length(params) != nargs)
196                 elog(ERROR, "EvaluateParams: wrong number of arguments");
197
198         exprstates = (List *) ExecPrepareExpr((Expr *) params, estate);
199
200         paramLI = (ParamListInfo)
201                 palloc0((nargs + 1) * sizeof(ParamListInfoData));
202
203         foreach(l, exprstates)
204         {
205                 ExprState  *n = lfirst(l);
206                 bool            isNull;
207
208                 paramLI[i].value = ExecEvalExprSwitchContext(n,
209                                                                                                          GetPerTupleExprContext(estate),
210                                                                                                          &isNull,
211                                                                                                          NULL);
212                 paramLI[i].kind = PARAM_NUM;
213                 paramLI[i].id = i + 1;
214                 paramLI[i].isnull = isNull;
215
216                 i++;
217         }
218
219         paramLI[i].kind = PARAM_INVALID;
220
221         return paramLI;
222 }
223
224
225 /*
226  * Initialize query hash table upon first use.
227  */
228 static void
229 InitQueryHashTable(void)
230 {
231         HASHCTL         hash_ctl;
232
233         MemSet(&hash_ctl, 0, sizeof(hash_ctl));
234
235         hash_ctl.keysize = HASH_KEY_LEN;
236         hash_ctl.entrysize = sizeof(QueryHashEntry);
237
238         prepared_queries = hash_create("Prepared Queries",
239                                                                    32,
240                                                                    &hash_ctl,
241                                                                    HASH_ELEM);
242
243         if (!prepared_queries)
244                 elog(ERROR, "InitQueryHashTable: unable to create hash table");
245 }
246
247 /*
248  * Store all the data pertaining to a query in the hash table using
249  * the specified key. A copy of the data is made in a memory context belonging
250  * to the hash entry, so the caller can dispose of their copy.
251  */
252 static void
253 StoreQuery(const char *stmt_name, List *query_list,
254                    List *plan_list, List *argtype_list)
255 {
256         QueryHashEntry *entry;
257         MemoryContext oldcxt,
258                                 entrycxt;
259         char            key[HASH_KEY_LEN];
260         bool            found;
261
262         /* Initialize the hash table, if necessary */
263         if (!prepared_queries)
264                 InitQueryHashTable();
265
266         /* Check for pre-existing entry of same name */
267         /* See notes in FetchQuery */
268         MemSet(key, 0, sizeof(key));
269         strncpy(key, stmt_name, sizeof(key));
270
271         hash_search(prepared_queries, key, HASH_FIND, &found);
272
273         if (found)
274                 elog(ERROR, "Prepared statement with name \"%s\" already exists",
275                          stmt_name);
276
277         /* Make a permanent memory context for the hashtable entry */
278         entrycxt = AllocSetContextCreate(TopMemoryContext,
279                                                                          stmt_name,
280                                                                          ALLOCSET_SMALL_MINSIZE,
281                                                                          ALLOCSET_SMALL_INITSIZE,
282                                                                          ALLOCSET_SMALL_MAXSIZE);
283
284         oldcxt = MemoryContextSwitchTo(entrycxt);
285
286         /*
287          * We need to copy the data so that it is stored in the correct memory
288          * context.  Do this before making hashtable entry, so that an
289          * out-of-memory failure only wastes memory and doesn't leave us with
290          * an incomplete (ie corrupt) hashtable entry.
291          */
292         query_list = (List *) copyObject(query_list);
293         plan_list = (List *) copyObject(plan_list);
294         argtype_list = listCopy(argtype_list);
295
296         /* Now we can add entry to hash table */
297         entry = (QueryHashEntry *) hash_search(prepared_queries,
298                                                                                    key,
299                                                                                    HASH_ENTER,
300                                                                                    &found);
301
302         /* Shouldn't get a failure, nor a duplicate entry */
303         if (!entry || found)
304                 elog(ERROR, "Unable to store prepared statement \"%s\"!",
305                          stmt_name);
306
307         /* Fill in the hash table entry with copied data */
308         entry->query_list = query_list;
309         entry->plan_list = plan_list;
310         entry->argtype_list = argtype_list;
311         entry->context = entrycxt;
312
313         MemoryContextSwitchTo(oldcxt);
314 }
315
316 /*
317  * Lookup an existing query in the hash table. If the query does not
318  * actually exist, an elog(ERROR) is thrown.
319  */
320 static QueryHashEntry *
321 FetchQuery(const char *plan_name)
322 {
323         char            key[HASH_KEY_LEN];
324         QueryHashEntry *entry;
325
326         /*
327          * If the hash table hasn't been initialized, it can't be storing
328          * anything, therefore it couldn't possibly store our plan.
329          */
330         if (!prepared_queries)
331                 elog(ERROR, "Prepared statement with name \"%s\" does not exist",
332                          plan_name);
333
334         /*
335          * We can't just use the statement name as supplied by the user: the
336          * hash package is picky enough that it needs to be NULL-padded out to
337          * the appropriate length to work correctly.
338          */
339         MemSet(key, 0, sizeof(key));
340         strncpy(key, plan_name, sizeof(key));
341
342         entry = (QueryHashEntry *) hash_search(prepared_queries,
343                                                                                    key,
344                                                                                    HASH_FIND,
345                                                                                    NULL);
346
347         if (!entry)
348                 elog(ERROR, "Prepared statement with name \"%s\" does not exist",
349                          plan_name);
350
351         return entry;
352 }
353
354 /*
355  * Given a plan name, look up the stored plan (giving error if not found).
356  * If found, return the list of argument type OIDs.
357  */
358 List *
359 FetchQueryParams(const char *plan_name)
360 {
361         QueryHashEntry *entry;
362
363         entry = FetchQuery(plan_name);
364
365         return entry->argtype_list;
366 }
367
368 /*
369  * Implements the 'DEALLOCATE' utility statement: deletes the
370  * specified plan from storage.
371  */
372 void
373 DeallocateQuery(DeallocateStmt *stmt)
374 {
375         QueryHashEntry *entry;
376
377         /* Find the query's hash table entry */
378         entry = FetchQuery(stmt->name);
379
380         /* Flush the context holding the subsidiary data */
381         Assert(MemoryContextIsValid(entry->context));
382         MemoryContextDelete(entry->context);
383
384         /* Now we can remove the hash table entry */
385         hash_search(prepared_queries, entry->key, HASH_REMOVE, NULL);
386 }
387
388 /*
389  * Implements the 'EXPLAIN EXECUTE' utility statement.
390  */
391 void
392 ExplainExecuteQuery(ExplainStmt *stmt, TupOutputState *tstate)
393 {
394         ExecuteStmt        *execstmt = (ExecuteStmt *) stmt->query->utilityStmt;
395         QueryHashEntry *entry;
396         List       *l,
397                            *query_list,
398                            *plan_list;
399         ParamListInfo paramLI = NULL;
400         EState     *estate = NULL;
401
402         /* explain.c should only call me for EXECUTE stmt */
403         Assert(execstmt && IsA(execstmt, ExecuteStmt));
404
405         /* Look it up in the hash table */
406         entry = FetchQuery(execstmt->name);
407
408         query_list = entry->query_list;
409         plan_list = entry->plan_list;
410
411         Assert(length(query_list) == length(plan_list));
412
413         /* Evaluate parameters, if any */
414         if (entry->argtype_list != NIL)
415         {
416                 /*
417                  * Need an EState to evaluate parameters; must not delete it
418                  * till end of query, in case parameters are pass-by-reference.
419                  */
420                 estate = CreateExecutorState();
421                 paramLI = EvaluateParams(estate, execstmt->params,
422                                                                  entry->argtype_list);
423         }
424
425         /* Explain each query */
426         foreach(l, query_list)
427         {
428                 Query     *query = (Query *) lfirst(l);
429                 Plan      *plan = (Plan *) lfirst(plan_list);
430                 bool            is_last_query;
431
432                 plan_list = lnext(plan_list);
433                 is_last_query = (plan_list == NIL);
434
435                 if (query->commandType == CMD_UTILITY)
436                 {
437                         if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
438                                 do_text_output_oneline(tstate, "NOTIFY");
439                         else
440                                 do_text_output_oneline(tstate, "UTILITY");
441                 }
442                 else
443                 {
444                         QueryDesc  *qdesc;
445
446                         /* Create a QueryDesc requesting no output */
447                         qdesc = CreateQueryDesc(query, plan, None, NULL,
448                                                                         paramLI, stmt->analyze);
449
450                         if (execstmt->into)
451                         {
452                                 if (qdesc->operation != CMD_SELECT)
453                                         elog(ERROR, "INTO clause specified for non-SELECT query");
454
455                                 query->into = execstmt->into;
456                                 qdesc->dest = None;
457                         }
458
459                         ExplainOnePlan(qdesc, stmt, tstate);
460                 }
461
462                 /* No need for CommandCounterIncrement, as ExplainOnePlan did it */
463
464                 /* put a blank line between plans */
465                 if (!is_last_query)
466                         do_text_output_oneline(tstate, "");
467         }
468
469         if (estate)
470                 FreeExecutorState(estate);
471 }