📄 pl_exec.c
字号:
*/ var = (PLpgSQL_var *) (estate.datums[func->tg_op_varno]); var->isnull = false; var->freeval = false; if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) var->value = DirectFunctionCall1(textin, CStringGetDatum("INSERT")); else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) var->value = DirectFunctionCall1(textin, CStringGetDatum("UPDATE")); else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) var->value = DirectFunctionCall1(textin, CStringGetDatum("DELETE")); else elog(ERROR, "unrecognized trigger action: not INSERT, DELETE, or UPDATE"); var = (PLpgSQL_var *) (estate.datums[func->tg_name_varno]); var->isnull = false; var->freeval = true; var->value = DirectFunctionCall1(namein, CStringGetDatum(trigdata->tg_trigger->tgname)); var = (PLpgSQL_var *) (estate.datums[func->tg_when_varno]); var->isnull = false; var->freeval = true; if (TRIGGER_FIRED_BEFORE(trigdata->tg_event)) var->value = DirectFunctionCall1(textin, CStringGetDatum("BEFORE")); else if (TRIGGER_FIRED_AFTER(trigdata->tg_event)) var->value = DirectFunctionCall1(textin, CStringGetDatum("AFTER")); else elog(ERROR, "unrecognized trigger execution time: not BEFORE or AFTER"); var = (PLpgSQL_var *) (estate.datums[func->tg_level_varno]); var->isnull = false; var->freeval = true; if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) var->value = DirectFunctionCall1(textin, CStringGetDatum("ROW")); else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event)) var->value = DirectFunctionCall1(textin, CStringGetDatum("STATEMENT")); else elog(ERROR, "unrecognized trigger event type: not ROW or STATEMENT"); var = (PLpgSQL_var *) (estate.datums[func->tg_relid_varno]); var->isnull = false; var->freeval = false; var->value = ObjectIdGetDatum(trigdata->tg_relation->rd_id); var = (PLpgSQL_var *) (estate.datums[func->tg_relname_varno]); var->isnull = false; var->freeval = true; var->value = DirectFunctionCall1(namein, CStringGetDatum(RelationGetRelationName(trigdata->tg_relation))); var = (PLpgSQL_var *) (estate.datums[func->tg_nargs_varno]); var->isnull = false; var->freeval = false; var->value = Int16GetDatum(trigdata->tg_trigger->tgnargs); /* * Store the actual call argument values into the special execution * state variables */ estate.err_text = gettext_noop("while storing call arguments into local variables"); estate.trig_nargs = trigdata->tg_trigger->tgnargs; if (estate.trig_nargs == 0) estate.trig_argv = NULL; else { estate.trig_argv = palloc(sizeof(Datum) * estate.trig_nargs); for (i = 0; i < trigdata->tg_trigger->tgnargs; i++) estate.trig_argv[i] = DirectFunctionCall1(textin, CStringGetDatum(trigdata->tg_trigger->tgargs[i])); } /* * Initialize the other variables to NULL values for now. The default * values are set when the blocks are entered. */ estate.err_text = gettext_noop("while initializing local variables to NULL"); for (i = estate.found_varno; i < estate.ndatums; i++) { switch (estate.datums[i]->dtype) { case PLPGSQL_DTYPE_VAR: { PLpgSQL_var *var = (PLpgSQL_var *) estate.datums[i]; var->value = 0; var->isnull = true; var->freeval = false; } break; case PLPGSQL_DTYPE_ROW: case PLPGSQL_DTYPE_REC: case PLPGSQL_DTYPE_RECFIELD: case PLPGSQL_DTYPE_ARRAYELEM: case PLPGSQL_DTYPE_TRIGARG: break; default: elog(ERROR, "unrecognized dtype: %d", func->datums[i]->dtype); } } /* * Set the magic variable FOUND to false */ exec_set_found(&estate, false); /* * Now call the toplevel block of statements */ estate.err_text = NULL; estate.err_stmt = (PLpgSQL_stmt *) (func->action); if (exec_stmt_block(&estate, func->action) != PLPGSQL_RC_RETURN) { estate.err_stmt = NULL; estate.err_text = NULL; ereport(ERROR, (errcode(ERRCODE_S_R_E_FUNCTION_EXECUTED_NO_RETURN_STATEMENT), errmsg("control reached end of trigger procedure without RETURN"))); } if (estate.retisset) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("trigger procedure cannot return a set"))); /* * Check that the returned tuple structure has the same attributes, * the relation that fired the trigger has. A per-statement trigger * always needs to return NULL, so we ignore any return value the * function itself produces (XXX: is this a good idea?) * * XXX This way it is possible, that the trigger returns a tuple where * attributes don't have the correct atttypmod's length. It's up to * the trigger's programmer to ensure that this doesn't happen. Jan */ if (estate.retisnull || TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event)) rettup = NULL; else { if (!compatible_tupdesc(estate.rettupdesc, trigdata->tg_relation->rd_att)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("returned tuple structure does not match table of trigger event"))); /* Copy tuple to upper executor memory */ rettup = SPI_copytuple((HeapTuple) (estate.retval)); } /* Clean up any leftover temporary memory */ if (estate.eval_econtext != NULL) FreeExprContext(estate.eval_econtext); estate.eval_econtext = NULL; exec_eval_cleanup(&estate); /* * Pop the error context stack */ error_context_stack = plerrcontext.previous; /* * Return the triggers result */ return rettup;}/* * error context callback to let us supply a call-stack traceback */static voidplpgsql_exec_error_callback(void *arg){ PLpgSQL_execstate *estate = (PLpgSQL_execstate *) arg; /* safety check, shouldn't happen */ if (estate->err_func == NULL) return; /* if we are doing RAISE, don't report its location */ if (estate->err_text == raise_skip_msg) return; if (estate->err_stmt != NULL) { /* translator: last %s is a plpgsql statement type name */ errcontext("PL/pgSQL function \"%s\" line %d at %s", estate->err_func->fn_name, estate->err_stmt->lineno, plpgsql_stmt_typename(estate->err_stmt)); } else if (estate->err_text != NULL) { /* * We don't expend the cycles to run gettext() on err_text unless * we actually need it. Therefore, places that set up err_text * should use gettext_noop() to ensure the strings get recorded in * the message dictionary. */ /* * translator: last %s is a phrase such as "while storing call * arguments into local variables" */ errcontext("PL/pgSQL function \"%s\" %s", estate->err_func->fn_name, gettext(estate->err_text)); } else errcontext("PL/pgSQL function \"%s\"", estate->err_func->fn_name);}/* ---------- * Support functions for copying local execution variables * ---------- */static PLpgSQL_var *copy_var(PLpgSQL_var * var){ PLpgSQL_var *new = palloc(sizeof(PLpgSQL_var)); memcpy(new, var, sizeof(PLpgSQL_var)); new->freeval = false; return new;}static PLpgSQL_rec *copy_rec(PLpgSQL_rec * rec){ PLpgSQL_rec *new = palloc(sizeof(PLpgSQL_rec)); memcpy(new, rec, sizeof(PLpgSQL_rec)); new->tup = NULL; new->tupdesc = NULL; new->freetup = false; new->freetupdesc = false; return new;}/* ---------- * exec_stmt_block Execute a block of statements * ---------- */static intexec_stmt_block(PLpgSQL_execstate * estate, PLpgSQL_stmt_block * block){ int rc; int i; int n; /* * First initialize all variables declared in this block */ for (i = 0; i < block->n_initvars; i++) { n = block->initvarnos[i]; switch (estate->datums[n]->dtype) { case PLPGSQL_DTYPE_VAR: { PLpgSQL_var *var = (PLpgSQL_var *) (estate->datums[n]); if (var->freeval) { pfree((void *) (var->value)); var->freeval = false; } if (!var->isconst || var->isnull) { if (var->default_val == NULL) { var->value = (Datum) 0; var->isnull = true; if (var->notnull) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("variable \"%s\" declared NOT NULL cannot default to NULL", var->refname))); } else { exec_assign_expr(estate, (PLpgSQL_datum *) var, var->default_val); } } } break; case PLPGSQL_DTYPE_REC: { PLpgSQL_rec *rec = (PLpgSQL_rec *) (estate->datums[n]); if (rec->freetup) { heap_freetuple(rec->tup); FreeTupleDesc(rec->tupdesc); rec->freetup = false; } rec->tup = NULL; rec->tupdesc = NULL; } break; case PLPGSQL_DTYPE_RECFIELD: case PLPGSQL_DTYPE_ARRAYELEM: break; default: elog(ERROR, "unrecognized dtype: %d", estate->datums[n]->dtype); } } /* * Execute the statements in the block's body */ rc = exec_stmts(estate, block->body); /* * Handle the return code. */ switch (rc) { case PLPGSQL_RC_OK: return PLPGSQL_RC_OK; case PLPGSQL_RC_EXIT: if (estate->exitlabel == NULL) return PLPGSQL_RC_OK; if (block->label == NULL) return PLPGSQL_RC_EXIT; if (strcmp(block->label, estate->exitlabel)) return PLPGSQL_RC_EXIT; estate->exitlabel = NULL; return PLPGSQL_RC_OK; case PLPGSQL_RC_RETURN: return PLPGSQL_RC_RETURN; default: elog(ERROR, "unrecognized rc: %d", rc); } return PLPGSQL_RC_OK;}/* ---------- * exec_stmts Iterate over a list of statements * as long as their return code is OK * ---------- */static intexec_stmts(PLpgSQL_execstate * estate, PLpgSQL_stmts * stmts){ int rc; int i; for (i = 0; i < stmts->stmts_used; i++) { rc = exec_stmt(estate, (PLpgSQL_stmt *) (stmts->stmts[i])); if (rc != PLPGSQL_RC_OK) return rc; } return PLPGSQL_RC_OK;}/* ---------- * exec_stmt Distribute one statement to the statements * type specific execution function. * ---------- */static intexec_stmt(PLpgSQL_execstate * estate, PLpgSQL_stmt * stmt){ PLpgSQL_stmt *save_estmt; int rc = -1; save_estmt = estate->err_stmt; estate->err_stmt = stmt; CHECK_FOR_INTERRUPTS(); switch (stmt->cmd_type) { case PLPGSQL_STMT_BLOCK: rc = exec_stmt_block(estate, (PLpgSQL_stmt_block *) stmt); break; case PLPGSQL_STMT_ASSIGN: rc = exec_stmt_assign(estate, (PLpgSQL_stmt_assign *) stmt); break; case PLPGSQL_STMT_PERFORM: rc = exec_stmt_perform(estate, (PLpgSQL_stmt_perform *) stmt); break; case PLPGSQL_STMT_GETDIAG: rc = exec_stmt_getdiag(estate, (PLpgSQL_stmt_getdiag *) stmt); break; case PLPGSQL_STMT_IF: rc = exec_stmt_if(estate, (PLpgSQL_stmt_if *) stmt); break; case PLPGSQL_STMT_LOOP: rc = exec_stmt_loop(estate, (PLpgSQL_stmt_loop *) stmt); break; case PLPGSQL_STMT_WHILE: rc = exec_stmt_while(estate, (PLpgSQL_stmt_while *) stmt); break; case PLPGSQL_STMT_FORI: rc = exec_stmt_fori(estate, (PLpgSQL_stmt_fori *) stmt); break; case PLPGSQL_STMT_FORS: rc = exec_stmt_fors(estate, (PLpgSQL_stmt_fors *) stmt); break; case PLPGSQL_STMT_SELECT: rc = exec_stmt_select(estate, (PLpgSQL_stmt_select *) stmt); break; case PLPGSQL_STMT_EXIT: rc = exec_stmt_exit(estate, (PLpgSQL_stmt_exit *) stmt); break; case PLPGSQL_STMT_RETURN: rc = exec_stmt_return(estate, (PLpgSQL_stmt_return *) stmt); break; case PLPGSQL_STMT_RETURN_NEXT: rc = exec_stmt_return_next(estate, (PLpgSQL_stmt_return_next *) stmt); break; case PLPGSQL_STMT_RAISE: rc = exec_stmt_raise(estate, (PLpgSQL_stmt_raise *) stmt); break; case PLPGSQL_STMT_EXECSQL: rc = exec_stmt_execsql(estate, (PLpgSQL_stmt_execsql *) stmt); break; case PLPGSQL_STMT_DYNEXECUTE: rc = exec_stmt_dynexecute(estate, (PLpgSQL_stmt_dynexecute *) stmt); break; case PLPGSQL_STMT_DYNFORS: rc = exec_stmt_dynfors(estate, (PLpgSQL_stmt_dynfors *) stmt); break; case PLPGSQL_STMT_OPEN: rc = exec_stmt_open(estate, (PLpgSQL_stmt_open *) stmt); break; case PLPGSQL_STMT_FETCH: rc = exec_stmt_fetch(estate, (PLpgSQL_stmt_fetch *) stmt); break; case PLPGSQL_STMT_CLOSE: rc = exec_stmt_close(estate, (PLpgSQL_stmt_close *) stmt); break; default: estate->err_stmt = save_estmt; elog(ERROR, "unrecognized cmdtype: %d", stmt->cmd_type); } estate->err_stmt = save_estmt; return rc;}/* ---------- * exec_stmt_assign Evaluate an expression and * put the result into a variable. * ---------- */static intexec_stmt_assign(PLpgSQL_execstate * estate, PLpgSQL_stmt_assign * stmt){ Assert(stmt->varno >= 0); exec_assign_expr(estate, estate->datums[stmt->varno], stmt->expr); return PLPGSQL_RC_OK;}/* ---------- * exec_stmt_perform Evaluate query and discard result (but set * FOUND depending on whether at least one row * was returned). * ---------- */static intexec_stmt_perform(PLpgSQL_execstate * estate, PLpgSQL_stmt_perform * stmt){ PLpgSQL_expr *expr = stmt->expr; int rc; /* * If not already done create a plan for this expression */ if (expr->plan == NULL) exec_prepare_plan(estate, expr); rc = exec_run_select(estate, expr, 0, NULL); if (rc != SPI_OK_SELECT) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("query \"%s\" did not return data", expr->query))); exec_set_found(estate, (estate->eval_processed != 0));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -