📄 pl_exec.c
字号:
elog(ERROR, "unrecognized trigger action: not INSERT, DELETE, or UPDATE"); /* * Assign the special tg_ variables */ var = (PLpgSQL_var *) (estate.datums[func->tg_op_varno]); 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->isnull = false; var->freeval = true; var = (PLpgSQL_var *) (estate.datums[func->tg_name_varno]); var->value = DirectFunctionCall1(namein, CStringGetDatum(trigdata->tg_trigger->tgname)); var->isnull = false; var->freeval = true; var = (PLpgSQL_var *) (estate.datums[func->tg_when_varno]); 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->isnull = false; var->freeval = true; var = (PLpgSQL_var *) (estate.datums[func->tg_level_varno]); 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->isnull = false; var->freeval = true; var = (PLpgSQL_var *) (estate.datums[func->tg_relid_varno]); var->value = ObjectIdGetDatum(trigdata->tg_relation->rd_id); var->isnull = false; var->freeval = false; var = (PLpgSQL_var *) (estate.datums[func->tg_relname_varno]); var->value = DirectFunctionCall1(namein, CStringGetDatum(RelationGetRelationName(trigdata->tg_relation))); var->isnull = false; var->freeval = true; var = (PLpgSQL_var *) (estate.datums[func->tg_nargs_varno]); var->value = Int16GetDatum(trigdata->tg_trigger->tgnargs); var->isnull = false; var->freeval = false; /* * Store the trigger 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])); } /* * 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); rc = exec_stmt_block(&estate, func->action); if (rc != PLPGSQL_RC_RETURN) { estate.err_stmt = NULL; estate.err_text = NULL; /* * Provide a more helpful message if a CONTINUE has been used outside * a loop. */ if (rc == PLPGSQL_RC_CONTINUE) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("CONTINUE cannot be used outside a loop"))); else 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 trigger's 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 function for initializing local execution variables * ---------- */static PLpgSQL_datum *copy_plpgsql_datum(PLpgSQL_datum *datum){ PLpgSQL_datum *result; switch (datum->dtype) { case PLPGSQL_DTYPE_VAR: { PLpgSQL_var *new = palloc(sizeof(PLpgSQL_var)); memcpy(new, datum, sizeof(PLpgSQL_var)); /* Ensure the value is null (possibly not needed?) */ new->value = 0; new->isnull = true; new->freeval = false; result = (PLpgSQL_datum *) new; } break; case PLPGSQL_DTYPE_REC: { PLpgSQL_rec *new = palloc(sizeof(PLpgSQL_rec)); memcpy(new, datum, sizeof(PLpgSQL_rec)); /* Ensure the value is null (possibly not needed?) */ new->tup = NULL; new->tupdesc = NULL; new->freetup = false; new->freetupdesc = false; result = (PLpgSQL_datum *) new; } break; case PLPGSQL_DTYPE_ROW: case PLPGSQL_DTYPE_RECFIELD: case PLPGSQL_DTYPE_ARRAYELEM: case PLPGSQL_DTYPE_TRIGARG: /* * These datum records are read-only at runtime, so no need to * copy them */ result = datum; break; default: elog(ERROR, "unrecognized dtype: %d", datum->dtype); result = NULL; /* keep compiler quiet */ break; } return result;}static boolexception_matches_conditions(ErrorData *edata, PLpgSQL_condition *cond){ for (; cond != NULL; cond = cond->next) { int sqlerrstate = cond->sqlerrstate; /* * OTHERS matches everything *except* query-canceled; if you're * foolish enough, you can match that explicitly. */ if (sqlerrstate == 0) { if (edata->sqlerrcode != ERRCODE_QUERY_CANCELED) return true; } /* Exact match? */ else if (edata->sqlerrcode == sqlerrstate) return true; /* Category match? */ else if (ERRCODE_IS_CATEGORY(sqlerrstate) && ERRCODE_TO_CATEGORY(edata->sqlerrcode) == sqlerrstate) return true; } return false;}/* ---------- * exec_stmt_block Execute a block of statements * ---------- */static intexec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block){ volatile int rc = -1; 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]); free_var(var); 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); } } if (block->exceptions) { /* * Execute the statements in the block's body inside a sub-transaction */ MemoryContext oldcontext = CurrentMemoryContext; ResourceOwner oldowner = CurrentResourceOwner; BeginInternalSubTransaction(NULL); /* Want to run statements inside function's memory context */ MemoryContextSwitchTo(oldcontext); PG_TRY(); { rc = exec_stmts(estate, block->body); /* Commit the inner transaction, return to outer xact context */ ReleaseCurrentSubTransaction(); MemoryContextSwitchTo(oldcontext); CurrentResourceOwner = oldowner; /* * AtEOSubXact_SPI() should not have popped any SPI context, but * just in case it did, make sure we remain connected. */ SPI_restore_connection(); } PG_CATCH(); { ErrorData *edata; ListCell *e; /* Save error info */ MemoryContextSwitchTo(oldcontext); edata = CopyErrorData(); FlushErrorState(); /* Abort the inner transaction */ RollbackAndReleaseCurrentSubTransaction(); MemoryContextSwitchTo(oldcontext); CurrentResourceOwner = oldowner; /* * If AtEOSubXact_SPI() popped any SPI context of the subxact, it * will have left us in a disconnected state. We need this hack * to return to connected state. */ SPI_restore_connection(); /* Look for a matching exception handler */ foreach(e, block->exceptions->exc_list) { PLpgSQL_exception *exception = (PLpgSQL_exception *) lfirst(e); if (exception_matches_conditions(edata, exception->conditions)) { /* * Initialize the magic SQLSTATE and SQLERRM variables for * the exception block. We needn't do this until we have * found a matching exception. */ PLpgSQL_var *state_var; PLpgSQL_var *errm_var; state_var = (PLpgSQL_var *) estate->datums[block->exceptions->sqlstate_varno]; state_var->value = DirectFunctionCall1(textin, CStringGetDatum(unpack_sql_state(edata->sqlerrcode))); state_var->freeval = true; state_var->isnull = false; errm_var = (PLpgSQL_var *) estate->datums[block->exceptions->sqlerrm_varno]; errm_var->value = DirectFunctionCall1(textin, CStringGetDatum(edata->message)); errm_var->freeval = true; errm_var->isnull = false; rc = exec_stmts(estate, exception->action); free_var(state_var); free_var(errm_var); break; } } /* If no match found, re-throw the error */ if (e == NULL) ReThrowError(edata); else FreeErrorData(edata); } PG_END_TRY(); } else { /* * Just execute the statements in the block's body */ rc = exec_stmts(estate, block->body); } /* * Handle the return code. */ switch (rc) { case PLPGSQL_RC_OK: case PLPGSQL_RC_CONTINUE: case PLPGSQL_RC_RETURN: return rc; 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; 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, List *stmts){ ListCell *s; if (stmts == NIL) { /* * Ensure we do a CHECK_FOR_INTERRUPTS() even though there is no * statement. This prevents hangup in a tight loop if, for instance, * there is a LOOP construct with an empty body. */ CHECK_FOR_INTERRUPTS(); return PLPGSQL_RC_OK; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -