📄 pl_exec.c
字号:
estate->retval = (Datum) 0; estate->rettupdesc = NULL; estate->retisnull = true; if (stmt->retrecno >= 0) { PLpgSQL_rec *rec = (PLpgSQL_rec *) (estate->datums[stmt->retrecno]); if (HeapTupleIsValid(rec->tup)) { estate->retval = (Datum) rec->tup; estate->rettupdesc = rec->tupdesc; estate->retisnull = false; } return PLPGSQL_RC_RETURN; } if (stmt->retrowno >= 0) { PLpgSQL_row *row = (PLpgSQL_row *) (estate->datums[stmt->retrowno]); if (row->rowtupdesc) /* should always be true here */ { estate->retval = (Datum) make_tuple_from_row(estate, row, row->rowtupdesc); if (estate->retval == (Datum) NULL) /* should not happen */ elog(ERROR, "row not compatible with its own tupdesc"); estate->rettupdesc = row->rowtupdesc; estate->retisnull = false; } return PLPGSQL_RC_RETURN; } if (stmt->expr != NULL) { exec_run_select(estate, stmt->expr, 1, NULL); if (estate->eval_processed > 0) { estate->retval = (Datum) estate->eval_tuptable->vals[0]; estate->rettupdesc = estate->eval_tuptable->tupdesc; estate->retisnull = false; } } return PLPGSQL_RC_RETURN; } if (estate->fn_rettype == VOIDOID) { /* Special hack for function returning VOID */ estate->retval = (Datum) 0; estate->retisnull = false; estate->rettype = VOIDOID; } else { /* Normal case for scalar results */ estate->retval = exec_eval_expr(estate, stmt->expr, &(estate->retisnull), &(estate->rettype)); } return PLPGSQL_RC_RETURN;}/* ---------- * exec_stmt_return_next Evaluate an expression and add it to the * list of tuples returned by the current * SRF. * ---------- */static intexec_stmt_return_next(PLpgSQL_execstate * estate, PLpgSQL_stmt_return_next * stmt){ TupleDesc tupdesc; int natts; HeapTuple tuple; bool free_tuple = false; if (!estate->retisset) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("cannot use RETURN NEXT in a non-SETOF function"))); if (estate->tuple_store == NULL) exec_init_tuple_store(estate); /* rettupdesc will be filled by exec_init_tuple_store */ tupdesc = estate->rettupdesc; natts = tupdesc->natts; if (stmt->rec) { PLpgSQL_rec *rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]); 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; } else if (stmt->row) { tuple = make_tuple_from_row(estate, stmt->row, tupdesc); if (tuple == NULL) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("wrong record type supplied in RETURN NEXT"))); free_tuple = true; } else if (stmt->expr) { Datum retval; bool isNull; Oid rettype; char nullflag; 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); nullflag = isNull ? 'n' : ' '; tuple = heap_formtuple(tupdesc, &retval, &nullflag); 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;}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, SortMem); 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){ Oid paramtypeid; Datum paramvalue; bool paramisnull; char *extval; int pidx = 0; char c[2] = {0, 0}; char *cp; PLpgSQL_dstring ds; plpgsql_dstring_init(&ds); for (cp = stmt->message; *cp; cp++) { /* * Occurences of a single % are replaced by the next argument's * external representation. Double %'s are converted to one %. */ if ((c[0] = *cp) == '%') { cp++; if (*cp == '%') { plpgsql_dstring_append(&ds, c); continue; } cp--; if (pidx >= stmt->nparams) { plpgsql_dstring_append(&ds, c); continue; } exec_eval_datum(estate, estate->datums[stmt->params[pidx]], InvalidOid, ¶mtypeid, ¶mvalue, ¶misnull); if (paramisnull) extval = "<NULL>"; else extval = convert_value_to_string(paramvalue, paramtypeid); plpgsql_dstring_append(&ds, extval); pidx++; continue; } /* * Occurrences of single ' are removed. double ' are reduced to * single ones. We must do this because the parameter stored by * the grammar is the raw T_STRING input literal, rather than the * de-lexed string as you might expect ... */ if (*cp == '\'') { cp++; if (*cp == '\'') plpgsql_dstring_append(&ds, c); else cp--; continue; } plpgsql_dstring_append(&ds, c); } /* * Throw the error (may or may not come back) */ estate->err_text = raise_skip_msg; /* suppress traceback of raise */ ereport(stmt->elog_level, (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->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->eval_econtext = NULL; estate->err_func = func; estate->err_stmt = NULL; estate->err_text = NULL;}/* ---------- * 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_exec */ 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 i; _SPI_plan *spi_plan; void *plan; Oid *argtypes; /* * We need a temporary argtypes array to load with data. (The finished * plan structure will contain a copy of it.) * * +1 is just to avoid palloc(0) error. */ argtypes = (Oid *) palloc(sizeof(Oid) * (expr->nparams + 1)); 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(expr->query, expr->nparams, argtypes); if (plan == NULL) elog(ERROR, "SPI_prepare() failed on \"%s\"", expr->query); expr->plan = SPI_saveplan(plan); spi_plan = (_SPI_plan *) expr->plan; expr->plan_argtypes = spi_plan->argtypes; expr->expr_simple_expr = NULL; exec_simple_check_plan(expr); SPI_freeplan(plan); pfree(argtypes);}/* ---------- * exec_stmt_execsql Execute an SQL statement not * returning any data. * ---------- */static intexec_stmt_execsql(PLpgSQL_execstate * estate, PLpgSQL_stmt_execsql * stmt){ int i; Datum *values; char *nulls; int rc; PLpgSQL_expr *expr = stmt->sqlstmt; /* * On the first call for this expression generate the plan */ if (expr->plan == NULL) exec_prepare_plan(estate, expr); /* * Now build up the values and nulls arguments for SPI_execp() */ values = palloc(sizeof(Datum) * (expr->nparams + 1)); nulls = palloc(expr->nparams + 1); 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] = ' '; } /* * Execute the plan */ rc = SPI_execp(expr->plan, values, nulls, 0); switch (rc) { case SPI_OK_UTILITY: case SPI_OK_SELINTO: break; case SPI_OK_INSERT: case SPI_OK_DELETE: case SPI_OK_UPDATE: /* * If the INSERT, DELETE, or UPDATE query affected at least * one tuple, set the magic 'FOUND' variable to true. This * conforms with the behavior of PL/SQL. */ exec_set_found(estate, (SPI_processed != 0)); break; case SPI_OK_SELECT: ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("SELECT query has no destination for result data"), errhint("If you want to discard the results, use PERFORM instead."))); default: elog(ERROR, "error executing query \"%s\"", expr->query); } /* * Release any result tuples from SPI_execp (probably shouldn't be * any) */ SPI_freetuptable(SPI_tuptable); /* Save result info for GET DIAGNOSTICS */ estate->eval_processed = SPI_processed; estate->eval_lastoid = SPI_lastoid; pfree(values); pfree(nulls); return PLPGSQL_RC_OK;}/* ---------- * exec_stmt_dynexecute Execute a dynamic SQL query not * returning any data. * ---------- */static intexec_stmt_dynexecute(PLpgSQL_execstate * estate, PLpgSQL_stmt_dynexecute * stmt){ Datum query; bool isnull = false; Oid restype; char *querystr; int exec_res; /* * First we evaluate the string expression after the EXECUTE keyword. * It's result is the querystring we have to execute. */ query = exec_eval_expr(estate, stmt->query, &isnull, &restype); if (isnull) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("cannot EXECUTE a null querystring"))); /* Get the C-String representation */ querystr = convert_value_to_string(query, restype); exec_eval_cleanup(estate); /* * Call SPI_exec() without preparing a saved plan. The returncode can * be any standard OK. Note that while a SELECT is allowed, its * results will be discarded. */ exec_res = SPI_exec(querystr, 0); switch (exec_res) { case SPI_OK_SELECT: case SPI_OK_INSERT: case SPI_OK_UPDATE: case SPI_OK_DELETE: case SPI_OK_UTILITY: break; case 0: /* * Also allow a zero return, which implies the querystring
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -