📄 pl_exec.c
字号:
/* ---------- * This is the field to change. Get it's type * and cast the value we insert to that type. * ---------- */ atttype = SPI_gettypeid(rec->tupdesc, i + 1); atttypmod = rec->tupdesc->attrs[i]->atttypmod; typetup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(atttype), 0, 0, 0); if (!HeapTupleIsValid(typetup)) elog(ERROR, "cache lookup for type %u failed", atttype); typeStruct = (Form_pg_type) GETSTRUCT(typetup); fmgr_info(typeStruct->typinput, &finfo_input); attisnull = *isNull; values[i] = exec_cast_value(value, valtype, atttype, &finfo_input, atttypmod, &attisnull); if (attisnull) nulls[i] = 'n'; else nulls[i] = ' '; } /* ---------- * Now call heap_formtuple() to create a new tuple * that replaces the old one in the record. * ---------- */ nulls[i] = '\0'; rec->tup = heap_formtuple(rec->tupdesc, values, nulls); pfree(values); pfree(nulls); break; default: elog(ERROR, "unknown dtype %d in exec_assign_value()", target->dtype); }}/* ---------- * exec_eval_expr Evaluate an expression and return * the result Datum. * ---------- */static Datumexec_eval_expr(PLpgSQL_execstate * estate, PLpgSQL_expr * expr, bool *isNull, Oid *rettype){ int rc; /* ---------- * If not already done create a plan for this expression * ---------- */ if (expr->plan == NULL) exec_prepare_plan(estate, expr); /* ---------- * If this is a simple expression, bypass SPI and use the * executor directly * ---------- */ if (expr->plan_simple_expr != NULL) return exec_eval_simple_expr(estate, expr, isNull, rettype); rc = exec_run_select(estate, expr, 2); if (rc != SPI_OK_SELECT) elog(ERROR, "query \"%s\" didn't return data", expr->query); /* ---------- * If there are no rows selected, the result is NULL. * ---------- */ if (SPI_processed == 0) { *isNull = true; return (Datum) 0; } /* ---------- * Check that the expression returned one single Datum * ---------- */ if (SPI_processed > 1) elog(ERROR, "query \"%s\" didn't return a single value", expr->query); if (SPI_tuptable->tupdesc->natts != 1) elog(ERROR, "query \"%s\" didn't return a single value", expr->query); /* ---------- * Return the result and it's type * ---------- */ *rettype = SPI_gettypeid(SPI_tuptable->tupdesc, 1); return SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, isNull);}/* ---------- * exec_run_select Execute a select query * ---------- */static intexec_run_select(PLpgSQL_execstate * estate, PLpgSQL_expr * expr, int maxtuples){ PLpgSQL_var *var; PLpgSQL_rec *rec; PLpgSQL_recfield *recfield; PLpgSQL_trigarg *trigarg; int tgargno; Oid tgargoid; int i; Datum *values; char *nulls; int rc; int fno; 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_eval_expr()", estate->datums[expr->params[i]]); } } nulls[i] = '\0'; /* ---------- * Execute the query * ---------- */ rc = SPI_execp(expr->plan, values, nulls, maxtuples); if (rc != SPI_OK_SELECT) elog(ERROR, "query \"%s\" isn't a SELECT", expr->query); pfree(values); pfree(nulls); return rc;}/* ---------- * exec_eval_simple_expr - Evaluate a simple expression returning * a Datum by directly calling ExecEvalExpr(). * ---------- */static Datumexec_eval_simple_expr(PLpgSQL_execstate * estate, PLpgSQL_expr * expr, bool *isNull, Oid *rettype){ Datum retval; PLpgSQL_var *var; PLpgSQL_rec *rec; PLpgSQL_recfield *recfield; PLpgSQL_trigarg *trigarg; int tgargno; Oid tgargoid; int fno; int i; bool isnull; bool isdone; ExprContext *econtext; ParamListInfo paramLI; /* ---------- * Create a simple expression context to hold the arguments * ---------- */ econtext = makeNode(ExprContext); paramLI = (ParamListInfo) palloc((expr->nparams + 1) * sizeof(ParamListInfoData)); econtext->ecxt_param_list_info = paramLI; /* ---------- * Put the parameter values into the parameter list info of * the expression context. * ---------- */ for (i = 0; i < expr->nparams; i++, paramLI++) { paramLI->kind = PARAM_NUM; paramLI->id = i + 1; switch (estate->datums[expr->params[i]]->dtype) { case PLPGSQL_DTYPE_VAR: var = (PLpgSQL_var *) (estate->datums[expr->params[i]]); paramLI->isnull = var->isnull; paramLI->value = var->value; 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); paramLI->value = SPI_getbinval(rec->tup, rec->tupdesc, fno, &isnull); paramLI->isnull = isnull; 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) { paramLI->value = 0; paramLI->isnull = TRUE; } else { paramLI->value = estate->trig_argv[tgargno]; paramLI->isnull = FALSE; } break; default: elog(ERROR, "unknown parameter dtype %d in exec_eval_simple_expr()", estate->datums[expr->params[i]]->dtype); } } paramLI->kind = PARAM_INVALID; /* ---------- * Initialize things * ---------- */ *isNull = FALSE; *rettype = expr->plan_simple_type; isdone = FALSE; /* ---------- * Clear the function cache * ---------- */ exec_eval_clear_fcache(expr->plan_simple_expr); /* ---------- * Now call the executor to evaluate the expression * ---------- */ SPI_push(); retval = ExecEvalExpr(expr->plan_simple_expr, econtext, isNull, &isdone); SPI_pop(); /* ---------- * That's it. * ---------- */ return retval;}/* ---------- * exec_move_row Move one tuples values into a * record or row * ---------- */static voidexec_move_row(PLpgSQL_execstate * estate, PLpgSQL_rec * rec, PLpgSQL_row * row, HeapTuple tup, TupleDesc tupdesc){ PLpgSQL_var *var; int i; Datum value; Oid valtype; bool isnull; /* ---------- * Record is simple - just put the tuple and it's descriptor * into the record * ---------- */ if (rec != NULL) { if (HeapTupleIsValid(tup)) { rec->tup = tup; rec->tupdesc = tupdesc; } else { rec->tup = NULL; rec->tupdesc = NULL; } return; } /* ---------- * Row is a bit more complicated in that we assign the single * attributes of the query to the variables the row points to. * ---------- */ if (row != NULL) { if (HeapTupleIsValid(tup)) { if (row->nfields != tupdesc->natts) { elog(ERROR, "query didn't return correct # of attributes for %s", row->refname); } for (i = 0; i < row->nfields; i++) { var = (PLpgSQL_var *) (estate->datums[row->varnos[i]]); valtype = SPI_gettypeid(tupdesc, i + 1); value = SPI_getbinval(tup, tupdesc, i + 1, &isnull); exec_assign_value(estate, estate->datums[row->varnos[i]], value, valtype, &isnull); } } else { for (i = 0; i < row->nfields; i++) { bool nullval = true; exec_assign_value(estate, estate->datums[row->varnos[i]], (Datum) 0, 0, &nullval); } } return; } elog(ERROR, "unsupported target in exec_move_row()");}/* ---------- * exec_cast_value Cast a value if required * ---------- */static Datumexec_cast_value(Datum value, Oid valtype, Oid reqtype, FmgrInfo *reqinput, int16 reqtypmod, bool *isnull){ if (!*isnull) { /* ---------- * If the type of the queries return value isn't * that of the variable, convert it. * ---------- */ if (valtype != reqtype || reqtypmod > 0) { HeapTuple typetup; Form_pg_type typeStruct; FmgrInfo finfo_output; char *extval; typetup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(valtype), 0, 0, 0); if (!HeapTupleIsValid(typetup)) elog(ERROR, "cache lookup for type %u failed", valtype); typeStruct = (Form_pg_type) GETSTRUCT(typetup); fmgr_info(typeStruct->typoutput, &finfo_output); extval = (char *) (*fmgr_faddr(&finfo_output)) (value, &isnull, -1); value = (Datum) (*fmgr_faddr(reqinput)) (extval, &isnull, reqtypmod); } } return value;}/* ---------- * exec_simple_check_node - Recursively check if an expression * is made only of simple things we can * hand out directly to ExecEvalExpr() * instead of calling SPI. * ---------- */static boolexec_simple_check_node(Node *node){ switch (nodeTag(node)) { case T_Expr: { Expr *expr = (Expr *) node; List *l; switch (expr->opType) { case OP_EXPR: case FUNC_EXPR: case OR_EXPR: case AND_EXPR: case NOT_EXPR: break; default: return FALSE; } foreach(l, expr->args) { if (!exec_simple_check_node(lfirst(l))) return FALSE; } return TRUE; } case T_Param: return TRUE; case T_Const: return TRUE; default: return FALSE; }}/* ---------- * exec_simple_check_plan - Check if a plan is simple enough to * be evaluated by ExecEvalExpr() instead * of SPI. * ---------- */static voidexec_simple_check_plan(PLpgSQL_expr * expr){ _SPI_plan *spi_plan = (_SPI_plan *) expr->plan; Plan *plan; TargetEntry *tle; expr->plan_simple_expr = NULL; /* ---------- * 1. We can only evaluate queries that resulted in one single * execution plan * ---------- */ if (length(spi_plan->ptlist) != 1) return; plan = (Plan *) lfirst(spi_plan->ptlist); /* ---------- * 2. It must be a RESULT plan --> no scan's required * ---------- */ if (plan == NULL) /* utility statement produces this */ return; if (nodeTag(plan) != T_Result) return; /* ---------- * 3. The plan must have a single attribute as result * ---------- */ if (length(plan->targetlist) != 1) return; /* ---------- * 4. Don't know if all these can break us, so let SPI handle * those plans * ---------- */ if (plan->qual != NULL || plan->lefttree != NULL || plan->righttree != NULL) return; /* ---------- * 5. Check that all the nodes in the expression are one of * Expr, Param or Const. * ---------- */ tle = (TargetEntry *) lfirst(plan->targetlist); if (!exec_simple_check_node(tle->expr)) return; /* ---------- * Yes - this is a simple expression. Remember the expression * and the return type * ---------- */ expr->plan_simple_expr = tle->expr; switch (nodeTag(tle->expr)) { case T_Expr: expr->plan_simple_type = ((Expr *) (tle->expr))->typeOid; break; case T_Param: expr->plan_simple_type = ((Param *) (tle->expr))->paramtype; break; case T_Const: expr->plan_simple_type = ((Const *) (tle->expr))->consttype; break; default: expr->plan_simple_type = InvalidOid; } return;}/* ---------- * exec_eval_clear_fcache - The function cache is palloc()'d by * the executor, and contains call specific * data based on the arguments. This has * to be recalculated. * ---------- */static voidexec_eval_clear_fcache(Node *node){ Expr *expr; List *l; if (nodeTag(node) != T_Expr) return; expr = (Expr *) node; switch (expr->opType) { case OP_EXPR: ((Oper *) (expr->oper))->op_fcache = NULL; break; case FUNC_EXPR: ((Func *) (expr->oper))->func_fcache = NULL; break; default: break; } foreach(l, expr->args) exec_eval_clear_fcache(lfirst(l));}/* ---------- * exec_set_found Set the global found variable * to true/false * ---------- */static voidexec_set_found(PLpgSQL_execstate * estate, bool state){ PLpgSQL_var *var; var = (PLpgSQL_var *) (estate->datums[estate->found_varno]); var->value = (Datum) state; var->isnull = false;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -