00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017 #include "postgres.h"
00018
00019 #include "access/xact.h"
00020 #include "catalog/pg_type.h"
00021 #include "commands/createas.h"
00022 #include "commands/prepare.h"
00023 #include "miscadmin.h"
00024 #include "nodes/nodeFuncs.h"
00025 #include "parser/analyze.h"
00026 #include "parser/parse_coerce.h"
00027 #include "parser/parse_collate.h"
00028 #include "parser/parse_expr.h"
00029 #include "parser/parse_type.h"
00030 #include "rewrite/rewriteHandler.h"
00031 #include "tcop/pquery.h"
00032 #include "tcop/utility.h"
00033 #include "utils/builtins.h"
00034 #include "utils/snapmgr.h"
00035 #include "utils/timestamp.h"
00036
00037
00038
00039
00040
00041
00042
00043
00044 static HTAB *prepared_queries = NULL;
00045
00046 static void InitQueryHashTable(void);
00047 static ParamListInfo EvaluateParams(PreparedStatement *pstmt, List *params,
00048 const char *queryString, EState *estate);
00049 static Datum build_regtype_array(Oid *param_types, int num_params);
00050
00051
00052
00053
00054 void
00055 PrepareQuery(PrepareStmt *stmt, const char *queryString)
00056 {
00057 CachedPlanSource *plansource;
00058 Oid *argtypes = NULL;
00059 int nargs;
00060 Query *query;
00061 List *query_list;
00062 int i;
00063
00064
00065
00066
00067
00068 if (!stmt->name || stmt->name[0] == '\0')
00069 ereport(ERROR,
00070 (errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
00071 errmsg("invalid statement name: must not be empty")));
00072
00073
00074
00075
00076
00077 plansource = CreateCachedPlan(stmt->query, queryString,
00078 CreateCommandTag(stmt->query));
00079
00080
00081 nargs = list_length(stmt->argtypes);
00082
00083 if (nargs)
00084 {
00085 ParseState *pstate;
00086 ListCell *l;
00087
00088
00089
00090
00091
00092 pstate = make_parsestate(NULL);
00093 pstate->p_sourcetext = queryString;
00094
00095 argtypes = (Oid *) palloc(nargs * sizeof(Oid));
00096 i = 0;
00097
00098 foreach(l, stmt->argtypes)
00099 {
00100 TypeName *tn = lfirst(l);
00101 Oid toid = typenameTypeId(pstate, tn);
00102
00103 argtypes[i++] = toid;
00104 }
00105 }
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115 query = parse_analyze_varparams((Node *) copyObject(stmt->query),
00116 queryString,
00117 &argtypes, &nargs);
00118
00119
00120
00121
00122 for (i = 0; i < nargs; i++)
00123 {
00124 Oid argtype = argtypes[i];
00125
00126 if (argtype == InvalidOid || argtype == UNKNOWNOID)
00127 ereport(ERROR,
00128 (errcode(ERRCODE_INDETERMINATE_DATATYPE),
00129 errmsg("could not determine data type of parameter $%d",
00130 i + 1)));
00131 }
00132
00133
00134
00135
00136 switch (query->commandType)
00137 {
00138 case CMD_SELECT:
00139 case CMD_INSERT:
00140 case CMD_UPDATE:
00141 case CMD_DELETE:
00142
00143 break;
00144 default:
00145 ereport(ERROR,
00146 (errcode(ERRCODE_INVALID_PSTATEMENT_DEFINITION),
00147 errmsg("utility statements cannot be prepared")));
00148 break;
00149 }
00150
00151
00152 query_list = QueryRewrite(query);
00153
00154
00155 CompleteCachedPlan(plansource,
00156 query_list,
00157 NULL,
00158 argtypes,
00159 nargs,
00160 NULL,
00161 NULL,
00162 0,
00163 true);
00164
00165
00166
00167
00168 StorePreparedStatement(stmt->name,
00169 plansource,
00170 true);
00171 }
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187 void
00188 ExecuteQuery(ExecuteStmt *stmt, IntoClause *intoClause,
00189 const char *queryString, ParamListInfo params,
00190 DestReceiver *dest, char *completionTag)
00191 {
00192 PreparedStatement *entry;
00193 CachedPlan *cplan;
00194 List *plan_list;
00195 ParamListInfo paramLI = NULL;
00196 EState *estate = NULL;
00197 Portal portal;
00198 char *query_string;
00199 int eflags;
00200 long count;
00201
00202
00203 entry = FetchPreparedStatement(stmt->name, true);
00204
00205
00206 if (!entry->plansource->fixed_result)
00207 elog(ERROR, "EXECUTE does not support variable-result cached plans");
00208
00209
00210 if (entry->plansource->num_params > 0)
00211 {
00212
00213
00214
00215
00216
00217
00218 estate = CreateExecutorState();
00219 estate->es_param_list_info = params;
00220 paramLI = EvaluateParams(entry, stmt->params,
00221 queryString, estate);
00222 }
00223
00224
00225 portal = CreateNewPortal();
00226
00227 portal->visible = false;
00228
00229
00230 query_string = MemoryContextStrdup(PortalGetHeapMemory(portal),
00231 entry->plansource->query_string);
00232
00233
00234 cplan = GetCachedPlan(entry->plansource, paramLI, false);
00235 plan_list = cplan->stmt_list;
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250 if (intoClause)
00251 {
00252 PlannedStmt *pstmt;
00253
00254 if (list_length(plan_list) != 1)
00255 ereport(ERROR,
00256 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
00257 errmsg("prepared statement is not a SELECT")));
00258 pstmt = (PlannedStmt *) linitial(plan_list);
00259 if (!IsA(pstmt, PlannedStmt) ||
00260 pstmt->commandType != CMD_SELECT ||
00261 pstmt->utilityStmt != NULL)
00262 ereport(ERROR,
00263 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
00264 errmsg("prepared statement is not a SELECT")));
00265
00266
00267 eflags = GetIntoRelEFlags(intoClause);
00268
00269
00270 if (intoClause->skipData)
00271 count = 0;
00272 else
00273 count = FETCH_ALL;
00274 }
00275 else
00276 {
00277
00278 eflags = 0;
00279 count = FETCH_ALL;
00280 }
00281
00282 PortalDefineQuery(portal,
00283 NULL,
00284 query_string,
00285 entry->plansource->commandTag,
00286 plan_list,
00287 cplan);
00288
00289
00290
00291
00292 PortalStart(portal, paramLI, eflags, GetActiveSnapshot());
00293
00294 (void) PortalRun(portal, count, false, dest, dest, completionTag);
00295
00296 PortalDrop(portal, false);
00297
00298 if (estate)
00299 FreeExecutorState(estate);
00300
00301
00302 }
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316 static ParamListInfo
00317 EvaluateParams(PreparedStatement *pstmt, List *params,
00318 const char *queryString, EState *estate)
00319 {
00320 Oid *param_types = pstmt->plansource->param_types;
00321 int num_params = pstmt->plansource->num_params;
00322 int nparams = list_length(params);
00323 ParseState *pstate;
00324 ParamListInfo paramLI;
00325 List *exprstates;
00326 ListCell *l;
00327 int i;
00328
00329 if (nparams != num_params)
00330 ereport(ERROR,
00331 (errcode(ERRCODE_SYNTAX_ERROR),
00332 errmsg("wrong number of parameters for prepared statement \"%s\"",
00333 pstmt->stmt_name),
00334 errdetail("Expected %d parameters but got %d.",
00335 num_params, nparams)));
00336
00337
00338 if (num_params == 0)
00339 return NULL;
00340
00341
00342
00343
00344
00345 params = (List *) copyObject(params);
00346
00347 pstate = make_parsestate(NULL);
00348 pstate->p_sourcetext = queryString;
00349
00350 i = 0;
00351 foreach(l, params)
00352 {
00353 Node *expr = lfirst(l);
00354 Oid expected_type_id = param_types[i];
00355 Oid given_type_id;
00356
00357 expr = transformExpr(pstate, expr, EXPR_KIND_EXECUTE_PARAMETER);
00358
00359 given_type_id = exprType(expr);
00360
00361 expr = coerce_to_target_type(pstate, expr, given_type_id,
00362 expected_type_id, -1,
00363 COERCION_ASSIGNMENT,
00364 COERCE_IMPLICIT_CAST,
00365 -1);
00366
00367 if (expr == NULL)
00368 ereport(ERROR,
00369 (errcode(ERRCODE_DATATYPE_MISMATCH),
00370 errmsg("parameter $%d of type %s cannot be coerced to the expected type %s",
00371 i + 1,
00372 format_type_be(given_type_id),
00373 format_type_be(expected_type_id)),
00374 errhint("You will need to rewrite or cast the expression.")));
00375
00376
00377 assign_expr_collations(pstate, expr);
00378
00379 lfirst(l) = expr;
00380 i++;
00381 }
00382
00383
00384 exprstates = (List *) ExecPrepareExpr((Expr *) params, estate);
00385
00386
00387 paramLI = (ParamListInfo)
00388 palloc(sizeof(ParamListInfoData) +
00389 (num_params - 1) * sizeof(ParamExternData));
00390
00391 paramLI->paramFetch = NULL;
00392 paramLI->paramFetchArg = NULL;
00393 paramLI->parserSetup = NULL;
00394 paramLI->parserSetupArg = NULL;
00395 paramLI->numParams = num_params;
00396
00397 i = 0;
00398 foreach(l, exprstates)
00399 {
00400 ExprState *n = lfirst(l);
00401 ParamExternData *prm = ¶mLI->params[i];
00402
00403 prm->ptype = param_types[i];
00404 prm->pflags = PARAM_FLAG_CONST;
00405 prm->value = ExecEvalExprSwitchContext(n,
00406 GetPerTupleExprContext(estate),
00407 &prm->isnull,
00408 NULL);
00409
00410 i++;
00411 }
00412
00413 return paramLI;
00414 }
00415
00416
00417
00418
00419
00420 static void
00421 InitQueryHashTable(void)
00422 {
00423 HASHCTL hash_ctl;
00424
00425 MemSet(&hash_ctl, 0, sizeof(hash_ctl));
00426
00427 hash_ctl.keysize = NAMEDATALEN;
00428 hash_ctl.entrysize = sizeof(PreparedStatement);
00429
00430 prepared_queries = hash_create("Prepared Queries",
00431 32,
00432 &hash_ctl,
00433 HASH_ELEM);
00434 }
00435
00436
00437
00438
00439
00440
00441
00442 void
00443 StorePreparedStatement(const char *stmt_name,
00444 CachedPlanSource *plansource,
00445 bool from_sql)
00446 {
00447 PreparedStatement *entry;
00448 TimestampTz cur_ts = GetCurrentStatementStartTimestamp();
00449 bool found;
00450
00451
00452 if (!prepared_queries)
00453 InitQueryHashTable();
00454
00455
00456 entry = (PreparedStatement *) hash_search(prepared_queries,
00457 stmt_name,
00458 HASH_ENTER,
00459 &found);
00460
00461
00462 if (found)
00463 ereport(ERROR,
00464 (errcode(ERRCODE_DUPLICATE_PSTATEMENT),
00465 errmsg("prepared statement \"%s\" already exists",
00466 stmt_name)));
00467
00468
00469 entry->plansource = plansource;
00470 entry->from_sql = from_sql;
00471 entry->prepare_time = cur_ts;
00472
00473
00474 SaveCachedPlan(plansource);
00475 }
00476
00477
00478
00479
00480
00481
00482
00483
00484 PreparedStatement *
00485 FetchPreparedStatement(const char *stmt_name, bool throwError)
00486 {
00487 PreparedStatement *entry;
00488
00489
00490
00491
00492
00493 if (prepared_queries)
00494 entry = (PreparedStatement *) hash_search(prepared_queries,
00495 stmt_name,
00496 HASH_FIND,
00497 NULL);
00498 else
00499 entry = NULL;
00500
00501 if (!entry && throwError)
00502 ereport(ERROR,
00503 (errcode(ERRCODE_UNDEFINED_PSTATEMENT),
00504 errmsg("prepared statement \"%s\" does not exist",
00505 stmt_name)));
00506
00507 return entry;
00508 }
00509
00510
00511
00512
00513
00514
00515
00516 TupleDesc
00517 FetchPreparedStatementResultDesc(PreparedStatement *stmt)
00518 {
00519
00520
00521
00522
00523 Assert(stmt->plansource->fixed_result);
00524 if (stmt->plansource->resultDesc)
00525 return CreateTupleDescCopy(stmt->plansource->resultDesc);
00526 else
00527 return NULL;
00528 }
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539 List *
00540 FetchPreparedStatementTargetList(PreparedStatement *stmt)
00541 {
00542 List *tlist;
00543
00544
00545 tlist = CachedPlanGetTargetList(stmt->plansource);
00546
00547
00548 return (List *) copyObject(tlist);
00549 }
00550
00551
00552
00553
00554
00555 void
00556 DeallocateQuery(DeallocateStmt *stmt)
00557 {
00558 if (stmt->name)
00559 DropPreparedStatement(stmt->name, true);
00560 else
00561 DropAllPreparedStatements();
00562 }
00563
00564
00565
00566
00567
00568
00569 void
00570 DropPreparedStatement(const char *stmt_name, bool showError)
00571 {
00572 PreparedStatement *entry;
00573
00574
00575 entry = FetchPreparedStatement(stmt_name, showError);
00576
00577 if (entry)
00578 {
00579
00580 DropCachedPlan(entry->plansource);
00581
00582
00583 hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
00584 }
00585 }
00586
00587
00588
00589
00590 void
00591 DropAllPreparedStatements(void)
00592 {
00593 HASH_SEQ_STATUS seq;
00594 PreparedStatement *entry;
00595
00596
00597 if (!prepared_queries)
00598 return;
00599
00600
00601 hash_seq_init(&seq, prepared_queries);
00602 while ((entry = hash_seq_search(&seq)) != NULL)
00603 {
00604
00605 DropCachedPlan(entry->plansource);
00606
00607
00608 hash_search(prepared_queries, entry->stmt_name, HASH_REMOVE, NULL);
00609 }
00610 }
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621 void
00622 ExplainExecuteQuery(ExecuteStmt *execstmt, IntoClause *into, ExplainState *es,
00623 const char *queryString, ParamListInfo params)
00624 {
00625 PreparedStatement *entry;
00626 const char *query_string;
00627 CachedPlan *cplan;
00628 List *plan_list;
00629 ListCell *p;
00630 ParamListInfo paramLI = NULL;
00631 EState *estate = NULL;
00632
00633
00634 entry = FetchPreparedStatement(execstmt->name, true);
00635
00636
00637 if (!entry->plansource->fixed_result)
00638 elog(ERROR, "EXPLAIN EXECUTE does not support variable-result cached plans");
00639
00640 query_string = entry->plansource->query_string;
00641
00642
00643 if (entry->plansource->num_params)
00644 {
00645
00646
00647
00648
00649
00650
00651 estate = CreateExecutorState();
00652 estate->es_param_list_info = params;
00653 paramLI = EvaluateParams(entry, execstmt->params,
00654 queryString, estate);
00655 }
00656
00657
00658 cplan = GetCachedPlan(entry->plansource, paramLI, true);
00659
00660 plan_list = cplan->stmt_list;
00661
00662
00663 foreach(p, plan_list)
00664 {
00665 PlannedStmt *pstmt = (PlannedStmt *) lfirst(p);
00666
00667 if (IsA(pstmt, PlannedStmt))
00668 ExplainOnePlan(pstmt, into, es, query_string, paramLI);
00669 else
00670 ExplainOneUtility((Node *) pstmt, into, es, query_string, paramLI);
00671
00672
00673
00674
00675 if (lnext(p) != NULL)
00676 ExplainSeparatePlans(es);
00677 }
00678
00679 if (estate)
00680 FreeExecutorState(estate);
00681
00682 ReleaseCachedPlan(cplan, true);
00683 }
00684
00685
00686
00687
00688
00689 Datum
00690 pg_prepared_statement(PG_FUNCTION_ARGS)
00691 {
00692 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
00693 TupleDesc tupdesc;
00694 Tuplestorestate *tupstore;
00695 MemoryContext per_query_ctx;
00696 MemoryContext oldcontext;
00697
00698
00699 if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
00700 ereport(ERROR,
00701 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00702 errmsg("set-valued function called in context that cannot accept a set")));
00703 if (!(rsinfo->allowedModes & SFRM_Materialize))
00704 ereport(ERROR,
00705 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
00706 errmsg("materialize mode required, but it is not " \
00707 "allowed in this context")));
00708
00709
00710 per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
00711 oldcontext = MemoryContextSwitchTo(per_query_ctx);
00712
00713
00714
00715
00716
00717 tupdesc = CreateTemplateTupleDesc(5, false);
00718 TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
00719 TEXTOID, -1, 0);
00720 TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement",
00721 TEXTOID, -1, 0);
00722 TupleDescInitEntry(tupdesc, (AttrNumber) 3, "prepare_time",
00723 TIMESTAMPTZOID, -1, 0);
00724 TupleDescInitEntry(tupdesc, (AttrNumber) 4, "parameter_types",
00725 REGTYPEARRAYOID, -1, 0);
00726 TupleDescInitEntry(tupdesc, (AttrNumber) 5, "from_sql",
00727 BOOLOID, -1, 0);
00728
00729
00730
00731
00732
00733 tupstore =
00734 tuplestore_begin_heap(rsinfo->allowedModes & SFRM_Materialize_Random,
00735 false, work_mem);
00736
00737
00738 MemoryContextSwitchTo(oldcontext);
00739
00740
00741 if (prepared_queries)
00742 {
00743 HASH_SEQ_STATUS hash_seq;
00744 PreparedStatement *prep_stmt;
00745
00746 hash_seq_init(&hash_seq, prepared_queries);
00747 while ((prep_stmt = hash_seq_search(&hash_seq)) != NULL)
00748 {
00749 Datum values[5];
00750 bool nulls[5];
00751
00752 MemSet(nulls, 0, sizeof(nulls));
00753
00754 values[0] = CStringGetTextDatum(prep_stmt->stmt_name);
00755 values[1] = CStringGetTextDatum(prep_stmt->plansource->query_string);
00756 values[2] = TimestampTzGetDatum(prep_stmt->prepare_time);
00757 values[3] = build_regtype_array(prep_stmt->plansource->param_types,
00758 prep_stmt->plansource->num_params);
00759 values[4] = BoolGetDatum(prep_stmt->from_sql);
00760
00761 tuplestore_putvalues(tupstore, tupdesc, values, nulls);
00762 }
00763 }
00764
00765
00766 tuplestore_donestoring(tupstore);
00767
00768 rsinfo->returnMode = SFRM_Materialize;
00769 rsinfo->setResult = tupstore;
00770 rsinfo->setDesc = tupdesc;
00771
00772 return (Datum) 0;
00773 }
00774
00775
00776
00777
00778
00779
00780 static Datum
00781 build_regtype_array(Oid *param_types, int num_params)
00782 {
00783 Datum *tmp_ary;
00784 ArrayType *result;
00785 int i;
00786
00787 tmp_ary = (Datum *) palloc(num_params * sizeof(Datum));
00788
00789 for (i = 0; i < num_params; i++)
00790 tmp_ary[i] = ObjectIdGetDatum(param_types[i]);
00791
00792
00793 result = construct_array(tmp_ary, num_params, REGTYPEOID, 4, true, 'i');
00794 return PointerGetDatum(result);
00795 }