📄 pl_exec.c
字号:
* 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_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 rows, 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 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) exec_init_tuple_store(estate); /* rettupdesc will be filled by exec_init_tuple_store */ tupdesc = estate->rettupdesc; natts = tupdesc->natts; if (stmt->retvarno >= 0) { PLpgSQL_datum *retvar = estate->datums[stmt->retvarno]; switch (retvar->dtype) { case PLPGSQL_DTYPE_VAR: { PLpgSQL_var *var = (PLpgSQL_var *) retvar; Datum retval = var->value; bool isNull = var->isnull; if (natts != 1) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("wrong result type supplied in RETURN NEXT"))); /* coerce type if needed */ retval = exec_simple_cast_value(retval, var->datatype->typoid, tupdesc->attrs[0]->atttypid, tupdesc->attrs[0]->atttypmod, isNull); tuple = heap_form_tuple(tupdesc, &retval, &isNull); free_tuple = true; } break; case PLPGSQL_DTYPE_REC: { PLpgSQL_rec *rec = (PLpgSQL_rec *) retvar; if (!HeapTupleIsValid(rec->tup)) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("record \"%s\" is not assigned yet", rec->refname), errdetail("The tuple structure of a not-yet-assigned record is indeterminate."))); if (!compatible_tupdesc(tupdesc, rec->tupdesc)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("wrong record type supplied in RETURN NEXT"))); tuple = rec->tup; } break; case PLPGSQL_DTYPE_ROW: { PLpgSQL_row *row = (PLpgSQL_row *) retvar; tuple = make_tuple_from_row(estate, row, tupdesc); if (tuple == NULL) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("wrong record type supplied in RETURN NEXT"))); free_tuple = true; } break; default: elog(ERROR, "unrecognized dtype: %d", retvar->dtype); tuple = NULL; /* keep compiler quiet */ break; } } else if (stmt->expr) { Datum retval; bool isNull; Oid rettype; if (natts != 1) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("wrong result type supplied in RETURN NEXT"))); retval = exec_eval_expr(estate, stmt->expr, &isNull, &rettype); /* coerce type if needed */ retval = exec_simple_cast_value(retval, rettype, tupdesc->attrs[0]->atttypid, tupdesc->attrs[0]->atttypmod, isNull); tuple = heap_form_tuple(tupdesc, &retval, &isNull); free_tuple = true; exec_eval_cleanup(estate); } else { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("RETURN NEXT must have a parameter"))); tuple = NULL; /* keep compiler quiet */ } if (HeapTupleIsValid(tuple)) { MemoryContext oldcxt; oldcxt = MemoryContextSwitchTo(estate->tuple_store_cxt); tuplestore_puttuple(estate->tuple_store, tuple); MemoryContextSwitchTo(oldcxt); if (free_tuple) heap_freetuple(tuple); } return PLPGSQL_RC_OK;}static voidexec_init_tuple_store(PLpgSQL_execstate *estate){ ReturnSetInfo *rsi = estate->rsi; MemoryContext oldcxt; /* * Check caller can handle a set result in the way we want */ if (!rsi || !IsA(rsi, ReturnSetInfo) || (rsi->allowedModes & SFRM_Materialize) == 0 || rsi->expectedDesc == NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); estate->tuple_store_cxt = rsi->econtext->ecxt_per_query_memory; oldcxt = MemoryContextSwitchTo(estate->tuple_store_cxt); estate->tuple_store = tuplestore_begin_heap(true, false, work_mem); MemoryContextSwitchTo(oldcxt); estate->rettupdesc = rsi->expectedDesc;}/* ---------- * exec_stmt_raise Build a message and throw it with elog() * ---------- */static intexec_stmt_raise(PLpgSQL_execstate *estate, PLpgSQL_stmt_raise *stmt){ char *cp;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -