📄 pl_exec.c
字号:
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 for evaluation of simple expressions, if there's not * one already in the current transaction. The EState is made a child of * TopTransactionContext so it will have the right lifespan. */ if (simple_eval_estate == NULL) { MemoryContext oldcontext; oldcontext = MemoryContextSwitchTo(TopTransactionContext); simple_eval_estate = CreateExecutorState(); MemoryContextSwitchTo(oldcontext); } /* * Create an expression context for simple expressions. This must be a * child of simple_eval_estate. */ estate->eval_econtext = CreateExprContext(simple_eval_estate);}/* ---------- * 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 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.) */ 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(expr->query, expr->nparams, argtypes); 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_CURSOR: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot manipulate cursors directly in PL/pgSQL"), errhint("Use PL/pgSQL's cursor features instead."))); 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 failed for \"%s\": %s", expr->query, SPI_result_code_string(SPI_result)); } } 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_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] = ' '; } /* * Execute the plan */ rc = SPI_execute_plan(expr->plan, values, nulls, estate->readonly_func, 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, "SPI_execute_plan failed executing query \"%s\": %s", expr->query, SPI_result_code_string(rc)); } /* * Release any result tuples from SPI_execute_plan (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; PLpgSQL_rec *rec = NULL; PLpgSQL_row *row = NULL; if (stmt->rec != NULL) rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]); else if (stmt->row != NULL) row = (PLpgSQL_row *) (estate->datums[stmt->row->rowno]); /* * 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_execute() without preparing a saved plan. The returncode can * be any standard OK. Note that while a SELECT is allowed, its results * will be discarded unless an INTO clause is specified. */ exec_res = SPI_execute(querystr, estate->readonly_func, 0); /* Assign to INTO variable */ if (rec || row) { if (exec_res != SPI_OK_SELECT) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("EXECUTE ... INTO is only for SELECT"))); else { if (SPI_processed == 0) exec_move_row(estate, rec, row, NULL, SPI_tuptable->tupdesc); else exec_move_row(estate, rec, row, SPI_tuptable->vals[0], SPI_tuptable->tupdesc); } } 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 * contained no commands. */ break; case SPI_OK_SELINTO: /* * We want to disallow SELECT INTO for now, because its behavior * is not consistent with SELECT INTO in a normal plpgsql context. * (We need to reimplement EXECUTE to parse the string as a * plpgsql command, not just feed it to SPI_execute.) However, * CREATE AS should be allowed ... and since it produces the same * parsetree as SELECT INTO, there's no way to tell the difference * except to look at the source text. Wotta kluge! */ { char *ptr; for (ptr = querystr; *ptr; ptr++) if (!isspace((unsigned char) *ptr)) break; if (*ptr == 'S' || *ptr == 's') ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("EXECUTE of SELECT ... INTO is not implemented yet"))); break; } /* Some SPI errors deserve specific error messages */ case SPI_ERROR_COPY: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot COPY to/from client in PL/pgSQL"))); case SPI_ERROR_CURSOR: ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot manipulate cursors directly in PL/pgSQL"), errhint("Use PL/pgSQL's cursor features instead."))); 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_execute failed executing query \"%s\": %s", querystr, SPI_result_code_string(exec_res)); break; } /* Release any result from SPI_execute, as well as the querystring */ SPI_freetuptable(SPI_tuptable); pfree(querystr); /* Save result info for GET DIAGNOSTICS */ estate->eval_processed = SPI_processed; estate->eval_lastoid = SPI_lastoid; return PLPGSQL_RC_OK;}/* ---------- * exec_stmt_dynfors Execute a dynamic query, assign each * tuple to a record or row and * execute a group of statements * for it. * ---------- */static intexec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt){ Datum query; bool isnull; Oid restype; char *querystr; PLpgSQL_rec *rec = NULL; PLpgSQL_row *row = NULL; SPITupleTable *tuptab; int n; void *plan; Portal portal; bool found = false; /* * Determine if we assign to a record or a row */ if (stmt->rec != NULL) rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]); else if (stmt->row != NULL) row = (PLpgSQL_row *) (estate->datums[stmt->row->rowno]); else elog(ERROR, "unsupported target"); /* * 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); /* * Prepare a plan and open an implicit cursor for the query */ plan = SPI_prepare(querystr, 0, NULL); if
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -