pl_exec.c
来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 2,523 行 · 第 1/5 页
C
2,523 行
exec_init_tuple_store(estate); /* rettupdesc will be filled by exec_init_tuple_store */ tupdesc = estate->rettupdesc; natts = tupdesc->natts; if (stmt->retvarno >= 0) { PLpgSQL_datum *retvar = estate->datums[stmt->retvarno]; switch (retvar->dtype) { case PLPGSQL_DTYPE_VAR: { PLpgSQL_var *var = (PLpgSQL_var *) retvar; Datum retval = var->value; bool isNull = var->isnull; if (natts != 1) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("wrong result type supplied in RETURN NEXT"))); /* coerce type if needed */ retval = exec_simple_cast_value(retval, var->datatype->typoid, tupdesc->attrs[0]->atttypid, tupdesc->attrs[0]->atttypmod, isNull); tuple = heap_form_tuple(tupdesc, &retval, &isNull); free_tuple = true; } break; case PLPGSQL_DTYPE_REC: { PLpgSQL_rec *rec = (PLpgSQL_rec *) retvar; if (!HeapTupleIsValid(rec->tup)) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("record \"%s\" is not assigned yet", rec->refname), errdetail("The tuple structure of a not-yet-assigned record is indeterminate."))); if (!compatible_tupdesc(tupdesc, rec->tupdesc)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("wrong record type supplied in RETURN NEXT"))); tuple = rec->tup; } break; case PLPGSQL_DTYPE_ROW: { PLpgSQL_row *row = (PLpgSQL_row *) retvar; tuple = make_tuple_from_row(estate, row, tupdesc); if (tuple == NULL) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("wrong record type supplied in RETURN NEXT"))); free_tuple = true; } break; default: elog(ERROR, "unrecognized dtype: %d", retvar->dtype); tuple = NULL; /* keep compiler quiet */ break; } } else if (stmt->expr) { Datum retval; bool isNull; Oid rettype; if (natts != 1) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("wrong result type supplied in RETURN NEXT"))); retval = exec_eval_expr(estate, stmt->expr, &isNull, &rettype); /* coerce type if needed */ retval = exec_simple_cast_value(retval, rettype, tupdesc->attrs[0]->atttypid, tupdesc->attrs[0]->atttypmod, isNull); tuple = heap_form_tuple(tupdesc, &retval, &isNull); free_tuple = true; exec_eval_cleanup(estate); } else { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("RETURN NEXT must have a parameter"))); tuple = NULL; /* keep compiler quiet */ } if (HeapTupleIsValid(tuple)) { MemoryContext oldcxt; oldcxt = MemoryContextSwitchTo(estate->tuple_store_cxt); tuplestore_puttuple(estate->tuple_store, tuple); MemoryContextSwitchTo(oldcxt); if (free_tuple) heap_freetuple(tuple); } return PLPGSQL_RC_OK;}/* ---------- * exec_stmt_return_query Evaluate a query and add it to the * list of tuples returned by the current * SRF. * ---------- */static intexec_stmt_return_query(PLpgSQL_execstate *estate, PLpgSQL_stmt_return_query *stmt){ Portal portal; if (!estate->retisset) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("cannot use RETURN QUERY in a non-SETOF function"))); if (estate->tuple_store == NULL) exec_init_tuple_store(estate); exec_run_select(estate, stmt->query, 0, &portal); if (!compatible_tupdesc(estate->rettupdesc, portal->tupDesc)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("structure of query does not match function result type"))); while (true) { MemoryContext old_cxt; int i; SPI_cursor_fetch(portal, true, 50); if (SPI_processed == 0) break; old_cxt = MemoryContextSwitchTo(estate->tuple_store_cxt); for (i = 0; i < SPI_processed; i++) { HeapTuple tuple = SPI_tuptable->vals[i]; tuplestore_puttuple(estate->tuple_store, tuple); } MemoryContextSwitchTo(old_cxt); SPI_freetuptable(SPI_tuptable); } SPI_freetuptable(SPI_tuptable); SPI_cursor_close(portal); return PLPGSQL_RC_OK;}static voidexec_init_tuple_store(PLpgSQL_execstate *estate){ ReturnSetInfo *rsi = estate->rsi; MemoryContext oldcxt; /* * Check caller can handle a set result in the way we want */ if (!rsi || !IsA(rsi, ReturnSetInfo) || (rsi->allowedModes & SFRM_Materialize) == 0 || rsi->expectedDesc == NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); estate->tuple_store_cxt = rsi->econtext->ecxt_per_query_memory; oldcxt = MemoryContextSwitchTo(estate->tuple_store_cxt); estate->tuple_store = tuplestore_begin_heap(true, false, work_mem); MemoryContextSwitchTo(oldcxt); estate->rettupdesc = rsi->expectedDesc;}/* ---------- * exec_stmt_raise Build a message and throw it with elog() * ---------- */static intexec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt){ char *cp; PLpgSQL_dstring ds; ListCell *current_param; plpgsql_dstring_init(&ds); current_param = list_head(stmt->params); for (cp = stmt->message; *cp; cp++) { /* * Occurrences of a single % are replaced by the next parameter's * external representation. Double %'s are converted to one %. */ if (cp[0] == '%') { Oid paramtypeid; Datum paramvalue; bool paramisnull; char *extval; if (cp[1] == '%') { plpgsql_dstring_append_char(&ds, cp[1]); cp++; continue; } if (current_param == NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("too few parameters specified for RAISE"))); paramvalue = exec_eval_expr(estate, (PLpgSQL_expr *) lfirst(current_param), ¶misnull, ¶mtypeid); if (paramisnull) extval = "<NULL>"; else extval = convert_value_to_string(paramvalue, paramtypeid); plpgsql_dstring_append(&ds, extval); current_param = lnext(current_param); exec_eval_cleanup(estate); continue; } plpgsql_dstring_append_char(&ds, cp[0]); } /* * If more parameters were specified than were required to process the * format string, throw an error */ if (current_param != NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("too many parameters specified for RAISE"))); /* * Throw the error (may or may not come back) */ estate->err_text = raise_skip_msg; /* suppress traceback of raise */ ereport(stmt->elog_level, ((stmt->elog_level >= ERROR) ? errcode(ERRCODE_RAISE_EXCEPTION) : 0, errmsg_internal("%s", plpgsql_dstring_get(&ds)))); estate->err_text = NULL; /* un-suppress... */ plpgsql_dstring_free(&ds); return PLPGSQL_RC_OK;}/* ---------- * Initialize a mostly empty execution state * ---------- */static voidplpgsql_estate_setup(PLpgSQL_execstate *estate, PLpgSQL_function *func, ReturnSetInfo *rsi){ estate->retval = (Datum) 0; estate->retisnull = true; estate->rettype = InvalidOid; estate->fn_rettype = func->fn_rettype; estate->retistuple = func->fn_retistuple; estate->retisset = func->fn_retset; estate->readonly_func = func->fn_readonly; estate->rettupdesc = NULL; estate->exitlabel = NULL; estate->tuple_store = NULL; estate->tuple_store_cxt = NULL; estate->rsi = rsi; estate->trig_nargs = 0; estate->trig_argv = NULL; estate->found_varno = func->found_varno; estate->ndatums = func->ndatums; estate->datums = palloc(sizeof(PLpgSQL_datum *) * estate->ndatums); /* caller is expected to fill the datums array */ estate->eval_tuptable = NULL; estate->eval_processed = 0; estate->eval_lastoid = InvalidOid; estate->err_func = func; estate->err_stmt = NULL; estate->err_text = NULL; /* * Create an EState and ExprContext for evaluation of simple expressions. */ plpgsql_create_econtext(estate); /* * Let the plugin see this function before we initialize any local * PL/pgSQL variables - note that we also give the plugin a few function * pointers so it can call back into PL/pgSQL for doing things like * variable assignments and stack traces */ if (*plugin_ptr) { (*plugin_ptr)->error_callback = plpgsql_exec_error_callback; (*plugin_ptr)->assign_expr = exec_assign_expr; if ((*plugin_ptr)->func_setup) ((*plugin_ptr)->func_setup) (estate, func); }}/* ---------- * Release temporary memory used by expression/subselect evaluation * * NB: the result of the evaluation is no longer valid after this is done, * unless it is a pass-by-value datatype. * ---------- */static voidexec_eval_cleanup(PLpgSQL_execstate *estate){ /* Clear result of a full SPI_execute */ if (estate->eval_tuptable != NULL) SPI_freetuptable(estate->eval_tuptable); estate->eval_tuptable = NULL; /* Clear result of exec_eval_simple_expr (but keep the econtext) */ if (estate->eval_econtext != NULL) ResetExprContext(estate->eval_econtext);}/* ---------- * Generate a prepared plan * ---------- */static voidexec_prepare_plan(PLpgSQL_execstate *estate, PLpgSQL_expr *expr, int cursorOptions){ int i; SPIPlanPtr plan; Oid *argtypes; /* * We need a temporary argtypes array to load with data. (The finished * plan structure will contain a copy of it.) */ argtypes = (Oid *) palloc(expr->nparams * sizeof(Oid)); for (i = 0; i < expr->nparams; i++) { Datum paramval; bool paramisnull; exec_eval_datum(estate, estate->datums[expr->params[i]], InvalidOid, &argtypes[i], ¶mval, ¶misnull); } /* * Generate and save the plan */ plan = SPI_prepare_cursor(expr->query, expr->nparams, argtypes, cursorOptions); if (plan == NULL) { /* Some SPI errors deserve specific error messages */ switch (SPI_result) { case SPI_ERROR_COPY: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot COPY to/from client in PL/pgSQL"))); case SPI_ERROR_TRANSACTION: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot begin/end transactions in PL/pgSQL"), errhint("Use a BEGIN block with an EXCEPTION clause instead."))); default: elog(ERROR, "SPI_prepare_cursor failed for \"%s\": %s", expr->query, SPI_result_code_string(SPI_result)); } } expr->plan = SPI_saveplan(plan); SPI_freeplan(plan); plan = expr->plan; expr->plan_argtypes = plan->argtypes; exec_simple_check_plan(expr); pfree(argtypes);}/* ---------- * exec_stmt_execsql Execute an SQL statement (possibly with INTO). * ---------- */static intexec_stmt_execsql(PLpgSQL_execstate *estate, PLpgSQL_stmt_execsql *stmt){ int i; Datum *values; char *nulls; long tcount; int rc; PLpgSQL_expr *expr = stmt->sqlstmt; /* * On the first call for this statement generate the plan, and detect * whether the statement is INSERT/UPDATE/DELETE */ if (expr->plan == NULL) { ListCell *l; exec_prepare_plan(estate, expr, 0); stmt->mod_stmt = false; foreach(l, expr->plan->plancache_list) { CachedPlanSource *plansource = (CachedPlanSource *) lfirst(l); ListCell *l2; foreach(l2, plansource->plan->stmt_list) { PlannedStmt *p = (PlannedStmt *) lfirst(l2); if (IsA(p, PlannedStmt) && p->canSetTag) { if (p->commandType == CMD_INSERT || p->commandType == CMD_UPDATE || p->commandType == CMD_DELETE) stmt->mod_stmt = true; } } } } /* * Now build up the values and nulls arguments for SPI_execute_plan() */ values = (Datum *) palloc(expr->nparams * sizeof(Datum)); nulls = (char *) palloc(expr->nparams * sizeof(char)); for (i = 0; i < expr->nparams; i++) { PLpgSQL_datum *datum = estate->datums[expr->params[i]]; Oid paramtypeid; bool paramisnull; exec_eval_datum(estate, datum, expr->plan_argtypes[i], ¶mtypeid, &values[i], ¶misnull); if (paramisnull) nulls[i] = 'n'; else nulls[i] = ' '; } /* * If we have INTO, then we only need one row back ... but if we have INTO * STRICT, ask for two rows, so that we can verify the statement returns * only one. INSERT/UPDATE/DELET
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?