pl_exec.c
来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 2,523 行 · 第 1/5 页
C
2,523 行
/* Commit the inner transaction, return to outer xact context */ ReleaseCurrentSubTransaction(); MemoryContextSwitchTo(oldcontext); CurrentResourceOwner = oldowner; /* Revert to outer eval_econtext */ estate->eval_econtext = old_eval_econtext; estate->eval_estate = old_eval_estate; estate->eval_estate_simple_id = old_eval_estate_simple_id; /* * 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; estate->err_text = gettext_noop("during exception cleanup"); /* Save error info */ MemoryContextSwitchTo(oldcontext); edata = CopyErrorData(); FlushErrorState(); /* Abort the inner transaction */ RollbackAndReleaseCurrentSubTransaction(); MemoryContextSwitchTo(oldcontext); CurrentResourceOwner = oldowner; /* Revert to outer eval_econtext */ estate->eval_econtext = old_eval_econtext; estate->eval_estate = old_eval_estate; estate->eval_estate_simple_id = old_eval_estate_simple_id; /* * 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; estate->err_text = NULL; rc = exec_stmts(estate, exception->action); free_var(state_var); state_var->value = (Datum) 0; free_var(errm_var); errm_var->value = (Datum) 0; 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 */ estate->err_text = NULL; rc = exec_stmts(estate, block->body); } estate->err_text = NULL; /* * 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; } foreach(s, stmts) { PLpgSQL_stmt *stmt = (PLpgSQL_stmt *) lfirst(s); int rc = exec_stmt(estate, stmt); 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; /* Let the plugin know that we are about to execute this statement */ if (*plugin_ptr && (*plugin_ptr)->stmt_beg) ((*plugin_ptr)->stmt_beg) (estate, 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_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_RETURN_QUERY: rc = exec_stmt_return_query(estate, (PLpgSQL_stmt_return_query *) 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); } /* Let the plugin know that we have finished executing this statement */ if (*plugin_ptr && (*plugin_ptr)->stmt_end) ((*plugin_ptr)->stmt_end) (estate, stmt); 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; (void) exec_run_select(estate, expr, 0, NULL); exec_set_found(estate, (estate->eval_processed != 0)); exec_eval_cleanup(estate); return PLPGSQL_RC_OK;}/* ---------- * exec_stmt_getdiag Put internal PG information into * specified variables. * ---------- */static intexec_stmt_getdiag(PLpgSQL_execstate *estate, PLpgSQL_stmt_getdiag *stmt){ ListCell *lc; foreach(lc, stmt->diag_items) { PLpgSQL_diag_item *diag_item = (PLpgSQL_diag_item *) lfirst(lc); PLpgSQL_datum *var; bool isnull = false; if (diag_item->target <= 0) continue; var = estate->datums[diag_item->target]; if (var == NULL) continue; switch (diag_item->kind) { case PLPGSQL_GETDIAG_ROW_COUNT: exec_assign_value(estate, var, UInt32GetDatum(estate->eval_processed), INT4OID, &isnull); break; case PLPGSQL_GETDIAG_RESULT_OID: exec_assign_value(estate, var, ObjectIdGetDatum(estate->eval_lastoid), OIDOID, &isnull); break; default: elog(ERROR, "unrecognized attribute request: %d", diag_item->kind); } } 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){ bool value; bool isnull; value = exec_eval_boolean(estate, stmt->cond, &isnull); exec_eval_cleanup(estate); if (!isnull && value) { if (stmt->true_body != NIL) return exec_stmts(estate, stmt->true_body); } else { if (stmt->false_body != NIL) 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){ for (;;) { int 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) != 0) return PLPGSQL_RC_EXIT; estate->exitlabel = NULL; return PLPGSQL_RC_OK; case PLPGSQL_RC_CONTINUE: if (estate->exitlabel == NULL) /* anonymous continue, so re-run the loop */ break; else if (stmt->label != NULL && strcmp(stmt->label, estate->exitlabel) == 0) /* label matches named continue, so re-run loop */ estate->exitlabel = NULL; else /* label doesn't match named continue, so propagate upward */ return PLPGSQL_RC_CONTINUE; break; case PLPGSQL_RC_RETURN: return PLPGSQL_RC_RETURN; default: elog(ERROR, "unrecognized rc: %d", 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){ for (;;) { int rc; bool value; bool isnull; value = exec_eval_boolean(estate, stmt->cond, &isnull); exec_eval_cleanup(estate); if (isnull || !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_CONTINUE: if (estate->exitlabel == NULL) /* anonymous continue, so re-run loop */ break; else if (stmt->label != NULL && strcmp(stmt->label, estate->exitlabel) == 0) /* label matches named continue, so re-run loop */ estate->exitlabel = NULL; else /* label doesn't match named continue, propagate upward */ return PLPGSQL_RC_CONTINUE; break; case PLPGSQL_RC_RETURN: return PLPGSQL_RC_RETURN; default: elog(ERROR, "unrecognized rc: %d", rc);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?