📄 pl_exec.c
字号:
} /* ---------- * There are tuples, so set found to true * ---------- */ exec_set_found(estate, true); /* ---------- * Now do the loop * ---------- */ tuptab = SPI_tuptable; SPI_tuptable = NULL; 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); /* ---------- * Check returncode * ---------- */ 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, "unknown rc %d from exec_stmts()", rc); } } return PLPGSQL_RC_OK;}/* ---------- * 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; int 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 in exec_stmt_select()"); } /* ---------- * Run the query * ---------- */ exec_run_select(estate, stmt->query, 1); n = SPI_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, NULL); return PLPGSQL_RC_OK; } /* ---------- * Put the result into the target and set found to true * ---------- */ tuptab = SPI_tuptable; SPI_tuptable = NULL; exec_move_row(estate, rec, row, tuptab->vals[0], tuptab->tupdesc); exec_set_found(estate, true); return PLPGSQL_RC_OK;}/* ---------- * exec_stmt_exit Start exiting loop(s) or blocks * ---------- */static intexec_stmt_exit(PLpgSQL_execstate * estate, PLpgSQL_stmt_exit * stmt){ Datum value; Oid valtype; bool isnull = false; /* ---------- * If the exit has a condition, check that it's true * ---------- */ if (stmt->cond != NULL) { value = exec_eval_expr(estate, stmt->cond, &isnull, &valtype); if (!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 (estate->retistuple) { if (stmt->retrecno >= 0) { PLpgSQL_rec *rec = (PLpgSQL_rec *) (estate->datums[stmt->retrecno]); estate->retval = (Datum) (rec->tup); estate->rettupdesc = rec->tupdesc; estate->retisnull = !HeapTupleIsValid(rec->tup); return PLPGSQL_RC_RETURN; } if (stmt->expr == NULL) { estate->retval = (Datum) 0; estate->rettupdesc = NULL; estate->retisnull = true; } else { exec_run_select(estate, stmt->expr, 1); estate->retval = (Datum) SPI_copytuple(SPI_tuptable->vals[0]); estate->rettupdesc = SPI_tuptable->tupdesc; estate->retisnull = false; } return PLPGSQL_RC_RETURN; } estate->retval = exec_eval_expr(estate, stmt->expr, &(estate->retisnull), &(estate->rettype)); return PLPGSQL_RC_RETURN;}/* ---------- * exec_stmt_raise Build a message and throw it with * elog() * ---------- */static intexec_stmt_raise(PLpgSQL_execstate * estate, PLpgSQL_stmt_raise * stmt){ HeapTuple typetup; Form_pg_type typeStruct; FmgrInfo finfo_output; char *extval; int pidx = 0; char c[2] = {0, 0}; char *cp; PLpgSQL_dstring ds; PLpgSQL_var *var; PLpgSQL_rec *rec; PLpgSQL_recfield *recfield; int fno; plpgsql_dstring_init(&ds); for (cp = stmt->message; *cp; cp++) { /* ---------- * Occurences of a single % are replaced by the next * arguments external representation. Double %'s are * left as is so elog() will also don't touch them. * ---------- */ if ((c[0] = *cp) == '%') { cp++; if (*cp == '%') { plpgsql_dstring_append(&ds, c); plpgsql_dstring_append(&ds, c); continue; } cp--; if (pidx >= stmt->nparams) { plpgsql_dstring_append(&ds, c); plpgsql_dstring_append(&ds, c); continue; } switch (estate->datums[stmt->params[pidx]]->dtype) { case PLPGSQL_DTYPE_VAR: var = (PLpgSQL_var *) (estate->datums[stmt->params[pidx]]); if (var->isnull) extval = "<NULL>"; else { typetup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(var->datatype->typoid), 0, 0, 0); if (!HeapTupleIsValid(typetup)) elog(ERROR, "cache lookup for type %u failed (1)", var->datatype->typoid); typeStruct = (Form_pg_type) GETSTRUCT(typetup); fmgr_info(typeStruct->typoutput, &finfo_output); extval = (char *) (*fmgr_faddr(&finfo_output)) (var->value, &(var->isnull), var->datatype->atttypmod); } plpgsql_dstring_append(&ds, extval); break; case PLPGSQL_DTYPE_RECFIELD: recfield = (PLpgSQL_recfield *) (estate->datums[stmt->params[pidx]]); rec = (PLpgSQL_rec *) (estate->datums[recfield->recno]); if (!HeapTupleIsValid(rec->tup)) extval = "<NULL>"; else { fno = SPI_fnumber(rec->tupdesc, recfield->fieldname); if (fno == SPI_ERROR_NOATTRIBUTE) elog(ERROR, "record %s has no field %s", rec->refname, recfield->fieldname); extval = SPI_getvalue(rec->tup, rec->tupdesc, fno); } plpgsql_dstring_append(&ds, extval); break; case PLPGSQL_DTYPE_TRIGARG: { PLpgSQL_trigarg *trigarg; int value; Oid valtype; bool valisnull = false; trigarg = (PLpgSQL_trigarg *) (estate->datums[stmt->params[pidx]]); value = (int) exec_eval_expr(estate, trigarg->argnum, &valisnull, &valtype); if (valisnull) extval = "<INDEX_IS_NULL>"; else { if (value < 0 || value >= estate->trig_nargs) extval = "<OUT_OF_RANGE>"; else extval = textout((text *) (estate->trig_argv[value])); } plpgsql_dstring_append(&ds, extval); } break; default: c[0] = '?'; plpgsql_dstring_append(&ds, c); break; } pidx++; continue; } /* ---------- * Occurences of single ' are removed. double ' are reduced * to single ones. * ---------- */ if (*cp == '\'') { cp++; if (*cp == '\'') plpgsql_dstring_append(&ds, c); else cp--; continue; } plpgsql_dstring_append(&ds, c); } /* ---------- * Now suppress debug info and throw the elog() * ---------- */ if (stmt->elog_level == ERROR) { error_info_func = NULL; error_info_stmt = NULL; error_info_text = NULL; } elog(stmt->elog_level, "%s", plpgsql_dstring_get(&ds)); plpgsql_dstring_free(&ds); return PLPGSQL_RC_OK;}/* ---------- * Generate a prepared plan * ---------- */static voidexec_prepare_plan(PLpgSQL_execstate * estate, PLpgSQL_expr * expr){ PLpgSQL_var *var; PLpgSQL_rec *rec; PLpgSQL_recfield *recfield; int i; int fno; void *plan; Oid *argtypes; /* ---------- * Setup the argtypes array * ---------- */ argtypes = malloc(sizeof(Oid *) * (expr->nparams + 1)); for (i = 0; i < expr->nparams; i++) { switch (estate->datums[expr->params[i]]->dtype) { case PLPGSQL_DTYPE_VAR: var = (PLpgSQL_var *) (estate->datums[expr->params[i]]); argtypes[i] = var->datatype->typoid; break; case PLPGSQL_DTYPE_RECFIELD: recfield = (PLpgSQL_recfield *) (estate->datums[expr->params[i]]); rec = (PLpgSQL_rec *) (estate->datums[recfield->recno]); if (!HeapTupleIsValid(rec->tup)) elog(ERROR, "record %s is unassigned yet", rec->refname); fno = SPI_fnumber(rec->tupdesc, recfield->fieldname); if (fno == SPI_ERROR_NOATTRIBUTE) elog(ERROR, "record %s has no field %s", rec->refname, recfield->fieldname); argtypes[i] = SPI_gettypeid(rec->tupdesc, fno); break; case PLPGSQL_DTYPE_TRIGARG: argtypes[i] = (Oid) TEXTOID; break; default: elog(ERROR, "unknown parameter dtype %d in exec_run_select()", estate->datums[expr->params[i]]); } } /* ---------- * Generate and save the plan * ---------- */ plan = SPI_prepare(expr->query, expr->nparams, argtypes); if (plan == NULL) elog(ERROR, "SPI_prepare() failed on \"%s\"", expr->query); expr->plan = SPI_saveplan(plan); expr->plan_argtypes = argtypes; expr->plan_simple_expr = NULL; exec_simple_check_plan(expr);}/* ---------- * exec_stmt_execsql Execute an SQL statement not * returning any data. * ---------- */static intexec_stmt_execsql(PLpgSQL_execstate * estate, PLpgSQL_stmt_execsql * stmt){ PLpgSQL_var *var; PLpgSQL_rec *rec; PLpgSQL_recfield *recfield; PLpgSQL_trigarg *trigarg; int tgargno; Oid tgargoid; int fno; int i; Datum *values; char *nulls; int rc; PLpgSQL_expr *expr = stmt->sqlstmt; bool isnull; /* ---------- * On the first call for this expression generate the plan * ---------- */ if (expr->plan == NULL) exec_prepare_plan(estate, expr); /* ---------- * Now build up the values and nulls arguments for SPI_execp() * ---------- */ values = palloc(sizeof(Datum) * (expr->nparams + 1)); nulls = palloc(expr->nparams + 1); for (i = 0; i < expr->nparams; i++) { switch (estate->datums[expr->params[i]]->dtype) { case PLPGSQL_DTYPE_VAR: var = (PLpgSQL_var *) (estate->datums[expr->params[i]]); values[i] = var->value; if (var->isnull) nulls[i] = 'n'; else nulls[i] = ' '; break; case PLPGSQL_DTYPE_RECFIELD: recfield = (PLpgSQL_recfield *) (estate->datums[expr->params[i]]); rec = (PLpgSQL_rec *) (estate->datums[recfield->recno]); if (!HeapTupleIsValid(rec->tup)) elog(ERROR, "record %s is unassigned yet", rec->refname); fno = SPI_fnumber(rec->tupdesc, recfield->fieldname); if (fno == SPI_ERROR_NOATTRIBUTE) elog(ERROR, "record %s has no field %s", rec->refname, recfield->fieldname); if (expr->plan_argtypes[i] != SPI_gettypeid(rec->tupdesc, fno)) elog(ERROR, "type of %s.%s doesn't match that when preparing the plan", rec->refname, recfield->fieldname); values[i] = SPI_getbinval(rec->tup, rec->tupdesc, fno, &isnull); if (isnull) nulls[i] = 'n'; else nulls[i] = ' '; break; case PLPGSQL_DTYPE_TRIGARG: trigarg = (PLpgSQL_trigarg *) (estate->datums[expr->params[i]]); tgargno = (int) exec_eval_expr(estate, trigarg->argnum, &isnull, &tgargoid); if (isnull || tgargno < 0 || tgargno >= estate->trig_nargs) { values[i] = 0; nulls[i] = 'n'; } else { values[i] = estate->trig_argv[tgargno]; nulls[i] = ' '; } break; default: elog(ERROR, "unknown parameter dtype %d in exec_stmt_execsql()", estate->datums[expr->params[i]]->dtype); } } nulls[i] = '\0'; /* ---------- * Execute the plan * ---------- */ rc = SPI_execp(expr->plan, values, nulls, 0); switch (rc) { case SPI_OK_UTILITY: case SPI_OK_SELINTO: case SPI_OK_INSERT: case SPI_OK_DELETE: case SPI_OK_UPDATE: break; case SPI_OK_SELECT: elog(ERROR, "unexpected SELECT query in exec_stmt_execsql()"); default: elog(ERROR, "error executing query \"%s\"", expr->query); } pfree(values); pfree(nulls); return PLPGSQL_RC_OK;}/* ---------- * exec_assign_expr Put an expressions result into * a variable. * ---------- */static voidexec_assign_expr(PLpgSQL_execstate * estate, PLpgSQL_datum * target, PLpgSQL_expr * expr){ Datum value; Oid valtype; bool isnull = false; value = exec_eval_expr(estate, expr, &isnull, &valtype); if (target != NULL) exec_assign_value(estate, target, value, valtype, &isnull);}/* ---------- * exec_assign_value Put a value into a target field * ---------- */static voidexec_assign_value(PLpgSQL_execstate * estate, PLpgSQL_datum * target, Datum value, Oid valtype, bool *isNull){ PLpgSQL_var *var; PLpgSQL_rec *rec; PLpgSQL_recfield *recfield; int fno; int i; int natts; Datum *values; char *nulls; bool attisnull; Oid atttype; int4 atttypmod; HeapTuple typetup; Form_pg_type typeStruct; FmgrInfo finfo_input; switch (target->dtype) { case PLPGSQL_DTYPE_VAR: /* ---------- * Target field is a variable - that's easy * ---------- */ var = (PLpgSQL_var *) target; var->value = exec_cast_value(value, valtype, var->datatype->typoid, &(var->datatype->typinput), var->datatype->atttypmod, isNull); if (isNull && var->notnull) elog(ERROR, "NULL assignment to variable '%s' declared NOT NULL", var->refname); var->isnull = *isNull; break; case PLPGSQL_DTYPE_RECFIELD: /* ---------- * Target field is a record * ---------- */ recfield = (PLpgSQL_recfield *) target; rec = (PLpgSQL_rec *) (estate->datums[recfield->recno]); /* ---------- * Check that there is already a tuple in the record. * We need that because records don't have any predefined * field structure. * ---------- */ if (!HeapTupleIsValid(rec->tup)) elog(ERROR, "record %s is unassigned yet - don't know it's tuple structure", rec->refname); /* ---------- * Get the number of the records field to change and the * number of attributes in the tuple. * ---------- */ fno = SPI_fnumber(rec->tupdesc, recfield->fieldname); if (fno == SPI_ERROR_NOATTRIBUTE) elog(ERROR, "record %s has no field %s", rec->refname, recfield->fieldname); fno--; natts = rec->tupdesc->natts; /* ---------- * We loop over the attributes of the rec's current tuple * and collect the values in a Datum array along with the * nulls information. * ---------- */ values = palloc(sizeof(Datum) * natts); nulls = palloc(natts + 1); for (i = 0; i < natts; i++) { /* ---------- * If this isn't the field we assign to, just use the * value that's already in the tuple. * ---------- */ if (i != fno) { values[i] = SPI_getbinval(rec->tup, rec->tupdesc, i + 1, &attisnull); if (attisnull) nulls[i] = 'n'; else nulls[i] = ' '; continue; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -