⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pl_exec.c

📁 关系型数据库 Postgresql 6.5.2
💻 C
📖 第 1 页 / 共 4 页
字号:
				/* ----------				 * 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 + -