pl_exec.c
来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 2,523 行 · 第 1/5 页
C
2,523 行
} } return PLPGSQL_RC_OK;}/* ---------- * exec_stmt_fori Iterate an integer variable * from a lower to an upper value * incrementing or decrementing by the BY value * ---------- */static intexec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt){ PLpgSQL_var *var; Datum value; bool isnull; Oid valtype; int32 loop_value; int32 end_value; int32 step_value; bool found = false; int rc = PLPGSQL_RC_OK; var = (PLpgSQL_var *) (estate->datums[stmt->var->varno]); /* * Get the value of the lower bound */ 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"))); loop_value = DatumGetInt32(value); 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"))); end_value = DatumGetInt32(value); exec_eval_cleanup(estate); /* * Get the step value */ if (stmt->step) { value = exec_eval_expr(estate, stmt->step, &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("BY value of FOR loop cannot be NULL"))); step_value = DatumGetInt32(value); exec_eval_cleanup(estate); if (step_value <= 0) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("BY value of FOR loop must be greater than zero"))); } else step_value = 1; /* * Now do the loop */ for (;;) { /* * Check against upper bound */ if (stmt->reverse) { if (loop_value < end_value) break; } else { if (loop_value > end_value) break; } found = true; /* looped at least once */ /* * Assign current value to loop var */ var->value = Int32GetDatum(loop_value); var->isnull = false; /* * 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) /* unlabelled 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 value, unless it would overflow, in which * case exit the loop. */ if (stmt->reverse) { if ((int32) (loop_value - step_value) > loop_value) break; loop_value -= step_value; } else { if ((int32) (loop_value + step_value) < loop_value) break; loop_value += step_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) { 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. */ } else if (rc == PLPGSQL_RC_CONTINUE) { if (estate->exitlabel == NULL) { /* anonymous continue, so re-run the current loop */ rc = PLPGSQL_RC_OK; continue; } else if (stmt->label != NULL && strcmp(stmt->label, estate->exitlabel) == 0) { /* label matches named continue, so re-run loop */ rc = PLPGSQL_RC_OK; estate->exitlabel = NULL; continue; } /* * otherwise, we processed 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. */ } /* * 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); 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_exit Implements EXIT and CONTINUE * * This begins the process of exiting / restarting a loop. * ---------- */static intexec_stmt_exit(PLpgSQL_execstate *estate, PLpgSQL_stmt_exit *stmt){ /* * If the exit / continue has a condition, evaluate it */ if (stmt->cond != NULL) { bool value; bool isnull; value = exec_eval_boolean(estate, stmt->cond, &isnull); exec_eval_cleanup(estate); if (isnull || value == false) return PLPGSQL_RC_OK; } estate->exitlabel = stmt->label; if (stmt->is_exit) return PLPGSQL_RC_EXIT; else return PLPGSQL_RC_CONTINUE;}/* ---------- * 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; /* initialize for null result (possibly a tuple) */ estate->retval = (Datum) 0; estate->rettupdesc = NULL; estate->retisnull = true; if (stmt->retvarno >= 0) { PLpgSQL_datum *retvar = estate->datums[stmt->retvarno]; switch (retvar->dtype) { case PLPGSQL_DTYPE_VAR: { PLpgSQL_var *var = (PLpgSQL_var *) retvar; estate->retval = var->value; estate->retisnull = var->isnull; estate->rettype = var->datatype->typoid; } break; case PLPGSQL_DTYPE_REC: { PLpgSQL_rec *rec = (PLpgSQL_rec *) retvar; if (HeapTupleIsValid(rec->tup)) { estate->retval = (Datum) rec->tup; estate->rettupdesc = rec->tupdesc; estate->retisnull = false; } } break; case PLPGSQL_DTYPE_ROW: { PLpgSQL_row *row = (PLpgSQL_row *) retvar; Assert(row->rowtupdesc); estate->retval = (Datum) make_tuple_from_row(estate, row, row->rowtupdesc); if (estate->retval == (Datum) NULL) /* should not happen */ elog(ERROR, "row not compatible with its own tupdesc"); estate->rettupdesc = row->rowtupdesc; estate->retisnull = false; } break; default: elog(ERROR, "unrecognized dtype: %d", retvar->dtype); } return PLPGSQL_RC_RETURN; } if (stmt->expr != NULL) { if (estate->retistuple) { exec_run_select(estate, stmt->expr, 1, NULL); if (estate->eval_processed > 0) { estate->retval = (Datum) estate->eval_tuptable->vals[0]; estate->rettupdesc = estate->eval_tuptable->tupdesc; estate->retisnull = false; } } else { /* Normal case for scalar results */ estate->retval = exec_eval_expr(estate, stmt->expr, &(estate->retisnull), &(estate->rettype)); } return PLPGSQL_RC_RETURN; } /* * Special hack for function returning VOID: instead of NULL, return a * non-null VOID value. This is of dubious importance but is kept for * backwards compatibility. Note that the only other way to get here is * to have written "RETURN NULL" in a function returning tuple. */ if (estate->fn_rettype == VOIDOID) { estate->retval = (Datum) 0; estate->retisnull = false; estate->rettype = VOIDOID; } return PLPGSQL_RC_RETURN;}/* ---------- * exec_stmt_return_next Evaluate an expression and add it to the * list of tuples returned by the current * SRF. * ---------- */static intexec_stmt_return_next(PLpgSQL_execstate *estate, PLpgSQL_stmt_return_next *stmt){ TupleDesc tupdesc; int natts; HeapTuple tuple; bool free_tuple = false; if (!estate->retisset) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("cannot use RETURN NEXT in a non-SETOF function"))); if (estate->tuple_store == NULL)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?