📄 pl_exec.c
字号:
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; 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; (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); } } 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; bool found = false; int rc = PLPGSQL_RC_OK; var = (PLpgSQL_var *) (estate->datums[stmt->var->varno]); /* * Get the value of the lower bound into the loop var */ value = exec_eval_expr(estate, stmt->lower, &isnull, &valtype); value = exec_cast_value(value, valtype, var->datatype->typoid, &(var->datatype->typinput), var->datatype->typioparam, var->datatype->atttypmod, isnull); if (isnull) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("lower bound of FOR loop cannot be NULL"))); var->value = value; var->isnull = false; exec_eval_cleanup(estate); /* * 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->typioparam, var->datatype->atttypmod, isnull); if (isnull) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("upper bound of FOR loop cannot be NULL"))); exec_eval_cleanup(estate); /* * Now do the loop */ for (;;) { /* * Check bounds */ if (stmt->reverse) { if ((int4) (var->value) < (int4) value) break; } else { if ((int4) (var->value) > (int4) value) break; } found = true; /* looped at least once */ /* * Execute the statements */ rc = exec_stmts(estate, stmt->body); if (rc == PLPGSQL_RC_RETURN) break; /* return from function */ else if (rc == PLPGSQL_RC_EXIT) { if (estate->exitlabel == NULL) /* unlabelled exit, finish the current loop */ rc = PLPGSQL_RC_OK; else if (stmt->label != NULL && strcmp(stmt->label, estate->exitlabel) == 0) { /* labelled exit, matches the current stmt's label */ estate->exitlabel = NULL; rc = PLPGSQL_RC_OK; } /* * otherwise, this is a labelled exit that does not match the * current statement's label, if any: return RC_EXIT so that the * EXIT continues to propagate up the stack. */ break; } else if (rc == PLPGSQL_RC_CONTINUE) { if (estate->exitlabel == NULL) /* anonymous continue, so re-run the current loop */ rc = PLPGSQL_RC_OK; else if (stmt->label != NULL && strcmp(stmt->label, estate->exitlabel) == 0) { /* label matches named continue, so re-run loop */ estate->exitlabel = NULL; rc = PLPGSQL_RC_OK; } else { /* * otherwise, this is a named continue that does not match the * current statement's label, if any: return RC_CONTINUE so * that the CONTINUE will propagate up the stack. */ break; } } /* * Increase/decrease loop var */ if (stmt->reverse) var->value--; else var->value++; } /* * Set the FOUND variable to indicate the result of executing the loop * (namely, whether we looped one or more times). This must be set here so * that it does not interfere with the value of the FOUND variable inside * the loop processing itself. */ exec_set_found(estate, found); return rc;}/* ---------- * 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; Portal portal; bool found = false; int rc = PLPGSQL_RC_OK; int i; int n; /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -