📄 pl_exec.c
字号:
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){ int i; PLpgSQL_datum *var; bool isnull = false; for (i = 0; i < stmt->ndtitems; i++) { PLpgSQL_diag_item *dtitem = &stmt->dtitems[i]; if (dtitem->target <= 0) continue; var = (estate->datums[dtitem->target]); if (var == NULL) continue; switch (dtitem->item) { 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", dtitem->item); } } 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 = false; value = exec_eval_boolean(estate, stmt->cond, &isnull); exec_eval_cleanup(estate); if (!isnull && 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, "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){ bool value; bool isnull = false; int rc; for (;;) { 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_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 = false; 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->typelem, 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->typelem, 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, we processed a labelled exit that does not match * the current statement's label, if any: return RC_EXIT so * that the EXIT continues to recurse upward. */ 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; /* * 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"); /* * Open the implicit cursor for the statement and fetch the initial 10 * rows. */ exec_run_select(estate, stmt->query, 0, &portal); SPI_cursor_fetch(portal, true, 10); tuptab = SPI_tuptable; n = SPI_processed; /* * If the query didn't return any rows, set the target to NULL and * return with FOUND = false. */ if (n == 0) exec_move_row(estate, rec, row, NULL, tuptab->tupdesc); else found = true; /* processed at least one tuple */ /* * Now do the loop */ while (n > 0) { for (i = 0; i < n; i++) { /* * Assign the tuple to the target */ exec_move_row(estate, rec, row, tuptab->vals[i], tuptab->tupdesc); /* * Execute the statements */ rc = exec_stmts(estate, stmt->body); if (rc != PLPGSQL_RC_OK) { /* * We're aborting the loop, so cleanup and set FOUND. * (This code should match the code after the loop.) */ SPI_freetuptable(tuptab); SPI_cursor_close(portal); exec_set_found(estate, found); 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, we processed a labelled exit that does * not match the current statement's label, if any: * return RC_EXIT so that the EXIT continues to * recurse upward. */ } return rc; } } SPI_freetuptable(tuptab); /* * Fetch the next 50 tuples */ SPI_cursor_fetch(portal, true, 50); n = SPI_processed; tuptab = SPI_tuptable; } /* * Release last group of tuples */ SPI_freetuptable(tuptab); /* * Close the implicit cursor */ SPI_cursor_close(portal); /* * 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_select Run a query and assign the first * row to a record or rowtype. * ---------- */static intexec_stmt_select(PLpgSQL_execstate * estate, PLpgSQL_stmt_select * stmt){ PLpgSQL_rec *rec = NULL; PLpgSQL_row *row = NULL; SPITupleTable *tuptab; uint32 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"); /* * Run the query */ exec_run_select(estate, stmt->query, 1, NULL); tuptab = estate->eval_tuptable; n = estate->eval_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, tuptab->tupdesc); exec_eval_cleanup(estate); return PLPGSQL_RC_OK; } /* * Put the result into the target and set found to true */ exec_move_row(estate, rec, row, tuptab->vals[0], tuptab->tupdesc); exec_set_found(estate, true); exec_eval_cleanup(estate); return PLPGSQL_RC_OK;}/* ---------- * exec_stmt_exit Start exiting loop(s) or blocks * ---------- */static intexec_stmt_exit(PLpgSQL_execstate * estate, PLpgSQL_stmt_exit * stmt){ /* * If the exit has a condition, check that it's true */ if (stmt->cond != NULL) { bool value; bool isnull = false; value = exec_eval_boolean(estate, stmt->cond, &isnull); exec_eval_cleanup(estate); if (isnull || !value) return PLPGSQL_RC_OK; } estate->exitlabel = stmt->label; return PLPGSQL_RC_EXIT;}/* ---------- * exec_stmt_return Evaluate an expression and start * returning from the function. * ---------- */static intexec_stmt_return(PLpgSQL_execstate * estate, PLpgSQL_stmt_return * stmt){ /* * If processing a set-returning PL/PgSQL function, the final RETURN * indicates that the function is finished producing tuples. The rest * of the work will be done at the top level. */ if (estate->retisset) return PLPGSQL_RC_RETURN; if (estate->retistuple) { /* initialize for null result tuple */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -