📄 pl_exec.c
字号:
else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event)) var->value = (Datum) textin("STATEMENT"); else var->value = (Datum) textin("UNKNOWN"); var = (PLpgSQL_var *) (estate.datums[func->tg_relid_varno]); var->isnull = false; var->value = (Datum) (trigdata->tg_relation->rd_id); var = (PLpgSQL_var *) (estate.datums[func->tg_relname_varno]); var->isnull = false; var->value = (Datum) namein(nameout(&(trigdata->tg_relation->rd_rel->relname))); var = (PLpgSQL_var *) (estate.datums[func->tg_nargs_varno]); var->isnull = false; var->value = (Datum) (trigdata->tg_trigger->tgnargs); /* ---------- * Put the actual call argument values into the special * execution state variables * ---------- */ error_info_text = "while putting call arguments to 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] = (Datum) textin(trigdata->tg_trigger->tgargs[i]); } /* ---------- * Initialize the other variables to NULL values for now. * The default values are set when the blocks are entered. * ---------- */ error_info_text = "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->shouldfree = false; } break; case PLPGSQL_DTYPE_ROW: case PLPGSQL_DTYPE_REC: case PLPGSQL_DTYPE_RECFIELD: case PLPGSQL_DTYPE_TRIGARG: break; default: elog(ERROR, "unknown dtype %d in plpgsql_exec_trigger()", func->datums[i]->dtype); } } /* ---------- * Set the magic variable FOUND to false * ---------- */ exec_set_found(&estate, false); /* ---------- * Now call the toplevel block of statements * ---------- */ error_info_text = NULL; error_info_stmt = (PLpgSQL_stmt *) (func->action); if (exec_stmt_block(&estate, func->action) != PLPGSQL_RC_RETURN) { error_info_stmt = NULL; error_info_text = "at END of toplevel PL block"; elog(ERROR, "control reaches end of trigger procedure without RETURN"); } /* ---------- * Check that the returned tuple structure has the same attributes, * the relation that fired the trigger has. * * 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) rettup = NULL; else { TupleDesc td1 = trigdata->tg_relation->rd_att; TupleDesc td2 = estate.rettupdesc; int i; if (td1->natts != td2->natts) elog(ERROR, "returned tuple structure doesn't match table of trigger event"); for (i = 1; i <= td1->natts; i++) { if (SPI_gettypeid(td1, i) != SPI_gettypeid(td2, i)) elog(ERROR, "returned tuple structure doesn't match table of trigger event"); } rettup = SPI_copytuple((HeapTuple) (estate.retval)); } /* ---------- * Restore the previous error info and elog() jump target * ---------- */ error_info_func = save_efunc; error_info_stmt = save_estmt; error_info_text = save_etext; memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart)); /* ---------- * Return the triggers result * ---------- */ return rettup;}/* ---------- * 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)); return new;}static PLpgSQL_rec *copy_rec(PLpgSQL_rec * rec){ PLpgSQL_rec *new = palloc(sizeof(PLpgSQL_rec)); memcpy(new, rec, sizeof(PLpgSQL_rec)); 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->isconst || var->isnull) { if (var->default_val == NULL) { var->value = (Datum) 0; var->isnull = true; if (var->notnull) elog(ERROR, "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]); rec->tup = NULL; rec->tupdesc = NULL; } break; case PLPGSQL_DTYPE_RECFIELD: break; default: elog(ERROR, "unknown dtype %d in exec_stmt_block()", 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, "unknown rc %d from exec_stmt()", 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 = error_info_stmt; error_info_stmt = stmt; 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_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_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; default: error_info_stmt = save_estmt; elog(ERROR, "unknown cmdtype %d in exec_stmt", stmt->cmd_type); } error_info_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){ if (stmt->varno < 0) exec_assign_expr(estate, NULL, stmt->expr); else exec_assign_expr(estate, estate->datums[stmt->varno], stmt->expr); return PLPGSQL_RC_OK;}/* ---------- * exec_stmt_if Evaluate a bool expression and * execute the true or false body * conditionally. * ---------- */static intexec_stmt_if(PLpgSQL_execstate * estate, PLpgSQL_stmt_if * stmt){ Datum value; Oid valtype; bool isnull = false; value = exec_eval_expr(estate, stmt->cond, &isnull, &valtype); if (value) { if (stmt->true_body != NULL) return exec_stmts(estate, stmt->true_body); } else { if (stmt->false_body != NULL) return exec_stmts(estate, stmt->false_body); } return PLPGSQL_RC_OK;}/* ---------- * exec_stmt_loop Loop over statements until * an exit occurs. * ---------- */static intexec_stmt_loop(PLpgSQL_execstate * estate, PLpgSQL_stmt_loop * stmt){ int rc; for (;;) { rc = exec_stmts(estate, stmt->body); switch (rc) { case PLPGSQL_RC_OK: break; case PLPGSQL_RC_EXIT: if (estate->exitlabel == NULL) return PLPGSQL_RC_OK; if (stmt->label == NULL) return PLPGSQL_RC_EXIT; if (strcmp(stmt->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, "unknown rc %d from exec_stmts()", rc); } } return PLPGSQL_RC_OK;}/* ---------- * exec_stmt_while Loop over statements as long * as an expression evaluates to * true or an exit occurs. * ---------- */static intexec_stmt_while(PLpgSQL_execstate * estate, PLpgSQL_stmt_while * stmt){ Datum value; Oid valtype; bool isnull = false; int rc; for (;;) { value = exec_eval_expr(estate, stmt->cond, &isnull, &valtype); if (!value) break; rc = exec_stmts(estate, stmt->body); switch (rc) { case PLPGSQL_RC_OK: break; case PLPGSQL_RC_EXIT: if (estate->exitlabel == NULL) return PLPGSQL_RC_OK; if (stmt->label == NULL) return PLPGSQL_RC_EXIT; if (strcmp(stmt->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, "unknown rc %d from exec_stmts()", rc); } } return PLPGSQL_RC_OK;}/* ---------- * exec_stmt_fori Iterate an integer variable * from a lower to an upper value. * Loop can be left with exit. * ---------- */static intexec_stmt_fori(PLpgSQL_execstate * estate, PLpgSQL_stmt_fori * stmt){ PLpgSQL_var *var; Datum value; Oid valtype; bool isnull = false; int rc; /* ---------- * Get the value of the lower bound into the loop var * ---------- */ value = exec_eval_expr(estate, stmt->lower, &isnull, &valtype); var = (PLpgSQL_var *) (estate->datums[stmt->var->varno]); value = exec_cast_value(value, valtype, var->datatype->typoid, &(var->datatype->typinput), var->datatype->atttypmod, &isnull); if (isnull) elog(ERROR, "lower bound of FOR loop cannot be NULL"); var->value = value; var->isnull = false; /* ---------- * Get the value of the upper bound * ---------- */ value = exec_eval_expr(estate, stmt->upper, &isnull, &valtype); value = exec_cast_value(value, valtype, var->datatype->typoid, &(var->datatype->typinput), var->datatype->atttypmod, &isnull); if (isnull) elog(ERROR, "upper bound of FOR loop cannot be NULL"); /* ---------- * Now do the loop * ---------- */ exec_set_found(estate, false); for (;;) { /* ---------- * Check bounds * ---------- */ if (stmt->reverse) { if ((int4) (var->value) < (int4) value) break; } else { if ((int4) (var->value) > (int4) value) break; } exec_set_found(estate, true); /* ---------- * Execute the statements * ---------- */ rc = exec_stmts(estate, stmt->body); /* ---------- * Check returncode * ---------- */ switch (rc) { case PLPGSQL_RC_OK: break; case PLPGSQL_RC_EXIT: if (estate->exitlabel == NULL) return PLPGSQL_RC_OK; if (stmt->label == NULL) return PLPGSQL_RC_EXIT; if (strcmp(stmt->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, "unknown rc %d from exec_stmts()", rc); } /* ---------- * Increase/decrease loop var * ---------- */ if (stmt->reverse) var->value--; else var->value++; } return PLPGSQL_RC_OK;}/* ---------- * exec_stmt_fors Execute a query, assign each * tuple to a record or row and * execute a group of statements * for it. * ---------- */static intexec_stmt_fors(PLpgSQL_execstate * estate, PLpgSQL_stmt_fors * stmt){ PLpgSQL_rec *rec = NULL; PLpgSQL_row *row = NULL; SPITupleTable *tuptab; int rc; int i; int n; /* ---------- * Initialize the global found variable to false * ---------- */ exec_set_found(estate, 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 in exec_stmt_fors()"); } /* ---------- * Run the query * ---------- */ exec_run_select(estate, stmt->query, 0); n = SPI_processed; /* ---------- * If the query didn't return any row, set the target * to NULL and return. * ---------- */ if (n == 0) { exec_move_row(estate, rec, row, NULL, NULL); return PLPGSQL_RC_OK;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -