pl_exec.c
来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 2,523 行 · 第 1/5 页
C
2,523 行
{ rec_new->tup = trigdata->tg_trigtuple; rec_new->tupdesc = trigdata->tg_relation->rd_att; rec_old->tup = NULL; rec_old->tupdesc = NULL; } else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) { rec_new->tup = trigdata->tg_newtuple; rec_new->tupdesc = trigdata->tg_relation->rd_att; rec_old->tup = trigdata->tg_trigtuple; rec_old->tupdesc = trigdata->tg_relation->rd_att; } else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) { rec_new->tup = NULL; rec_new->tupdesc = NULL; rec_old->tup = trigdata->tg_trigtuple; rec_old->tupdesc = trigdata->tg_relation->rd_att; } else 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_table_name_varno]); var->value = DirectFunctionCall1(namein, CStringGetDatum(RelationGetRelationName(trigdata->tg_relation))); var->isnull = false; var->freeval = true; var = (PLpgSQL_var *) (estate.datums[func->tg_table_schema_varno]); var->value = DirectFunctionCall1(namein, CStringGetDatum( get_namespace_name( RelationGetNamespace( 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])); } estate.err_text = gettext_noop("during function entry"); /* * Set the magic variable FOUND to false */ exec_set_found(&estate, false); /* * Let the instrumentation plugin peek at this function */ if (*plugin_ptr && (*plugin_ptr)->func_beg) ((*plugin_ptr)->func_beg) (&estate, func); /* * 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"))); } estate.err_stmt = NULL; estate.err_text = gettext_noop("during function exit"); 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)); } /* * Let the instrumentation plugin peek at this function */ if (*plugin_ptr && (*plugin_ptr)->func_end) ((*plugin_ptr)->func_end) (&estate, func); /* Clean up any leftover temporary memory */ 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_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. * * If both err_text and err_stmt are set, use the err_text as * description, but report the err_stmt's line number. When err_stmt * is not set, we're in function entry/exit, or some such place not * attached to a specific line number. */ if (estate->err_stmt != NULL) { /* * translator: last %s is a phrase such as "during statement block * local variable initialization" */ errcontext("PL/pgSQL function \"%s\" line %d %s", estate->err_func->fn_name, estate->err_stmt->lineno, gettext(estate->err_text)); } else { /* * 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 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 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 */ estate->err_text = gettext_noop("during statement block local variable initialization"); 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 any old value, in case re-entering block */ free_var(var); /* Initially it contains a NULL */ var->value = (Datum) 0; var->isnull = true; if (var->default_val == NULL) { /* * If needed, give the datatype a chance to reject * NULLs, by assigning a NULL to the variable. We * claim the value is of type UNKNOWN, not the var's * datatype, else coercion will be skipped. (Do this * before the notnull check to be consistent with * exec_assign_value.) */ if (!var->datatype->typinput.fn_strict) { bool valIsNull = true; exec_assign_value(estate, (PLpgSQL_datum *) var, (Datum) 0, UNKNOWNOID, &valIsNull); } 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; ExprContext *old_eval_econtext = estate->eval_econtext; EState *old_eval_estate = estate->eval_estate; long int old_eval_estate_simple_id = estate->eval_estate_simple_id; estate->err_text = gettext_noop("during statement block entry"); BeginInternalSubTransaction(NULL); /* Want to run statements inside function's memory context */ MemoryContextSwitchTo(oldcontext); PG_TRY(); { /* * We need to run the block's statements with a new eval_econtext * that belongs to the current subtransaction; if we try to use * the outer econtext then ExprContext shutdown callbacks will be * called at the wrong times. */ plpgsql_create_econtext(estate); estate->err_text = NULL; /* Run the block's statements */ rc = exec_stmts(estate, block->body); estate->err_text = gettext_noop("during statement block exit"); /* * If the block ended with RETURN, we may need to copy the return * value out of the subtransaction eval_context. This is * currently only needed for scalar result types --- rowtype * values will always exist in the function's own memory context. */ if (rc == PLPGSQL_RC_RETURN && !estate->retisset && !estate->retisnull && estate->rettupdesc == NULL) { int16 resTypLen; bool resTypByVal; get_typlenbyval(estate->rettype, &resTypLen, &resTypByVal); estate->retval = datumCopy(estate->retval, resTypByVal, resTypLen); }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?