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

📄 pl_exec.c

📁 PostgreSQL7.4.6 for Linux
💻 C
📖 第 1 页 / 共 5 页
字号:
		estate->retval = (Datum) 0;		estate->rettupdesc = NULL;		estate->retisnull = true;		if (stmt->retrecno >= 0)		{			PLpgSQL_rec *rec = (PLpgSQL_rec *) (estate->datums[stmt->retrecno]);			if (HeapTupleIsValid(rec->tup))			{				estate->retval = (Datum) rec->tup;				estate->rettupdesc = rec->tupdesc;				estate->retisnull = false;			}			return PLPGSQL_RC_RETURN;		}		if (stmt->retrowno >= 0)		{			PLpgSQL_row *row = (PLpgSQL_row *) (estate->datums[stmt->retrowno]);			if (row->rowtupdesc) /* should always be true here */			{				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;			}			return PLPGSQL_RC_RETURN;		}		if (stmt->expr != NULL)		{			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;			}		}		return PLPGSQL_RC_RETURN;	}	if (estate->fn_rettype == VOIDOID)	{		/* Special hack for function returning VOID */		estate->retval = (Datum) 0;		estate->retisnull = false;		estate->rettype = VOIDOID;	}	else	{		/* Normal case for scalar results */		estate->retval = exec_eval_expr(estate, stmt->expr,										&(estate->retisnull),										&(estate->rettype));	}	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->rec)	{		PLpgSQL_rec *rec = (PLpgSQL_rec *) (estate->datums[stmt->rec->recno]);		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;	}	else if (stmt->row)	{		tuple = make_tuple_from_row(estate, stmt->row, tupdesc);		if (tuple == NULL)			ereport(ERROR,					(errcode(ERRCODE_DATATYPE_MISMATCH),					 errmsg("wrong record type supplied in RETURN NEXT")));		free_tuple = true;	}	else if (stmt->expr)	{		Datum		retval;		bool		isNull;		Oid			rettype;		char		nullflag;		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);		nullflag = isNull ? 'n' : ' ';		tuple = heap_formtuple(tupdesc, &retval, &nullflag);		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, SortMem);	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){	Oid			paramtypeid;	Datum		paramvalue;	bool		paramisnull;	char	   *extval;	int			pidx = 0;	char		c[2] = {0, 0};	char	   *cp;	PLpgSQL_dstring ds;	plpgsql_dstring_init(&ds);	for (cp = stmt->message; *cp; cp++)	{		/*		 * Occurences of a single % are replaced by the next argument's		 * external representation. Double %'s are converted to one %.		 */		if ((c[0] = *cp) == '%')		{			cp++;			if (*cp == '%')			{				plpgsql_dstring_append(&ds, c);				continue;			}			cp--;			if (pidx >= stmt->nparams)			{				plpgsql_dstring_append(&ds, c);				continue;			}			exec_eval_datum(estate, estate->datums[stmt->params[pidx]],							InvalidOid,							&paramtypeid, &paramvalue, &paramisnull);			if (paramisnull)				extval = "<NULL>";			else				extval = convert_value_to_string(paramvalue, paramtypeid);			plpgsql_dstring_append(&ds, extval);			pidx++;			continue;		}		/*		 * Occurrences of single ' are removed. double ' are reduced to		 * single ones.  We must do this because the parameter stored by		 * the grammar is the raw T_STRING input literal, rather than the		 * de-lexed string as you might expect ...		 */		if (*cp == '\'')		{			cp++;			if (*cp == '\'')				plpgsql_dstring_append(&ds, c);			else				cp--;			continue;		}		plpgsql_dstring_append(&ds, c);	}	/*	 * Throw the error (may or may not come back)	 */	estate->err_text = raise_skip_msg;	/* suppress traceback of raise */	ereport(stmt->elog_level,			(errmsg_internal("%s", plpgsql_dstring_get(&ds))));	estate->err_text = NULL;	/* un-suppress... */	plpgsql_dstring_free(&ds);	return PLPGSQL_RC_OK;}/* ---------- * Initialize a mostly empty execution state * ---------- */static voidplpgsql_estate_setup(PLpgSQL_execstate * estate,					 PLpgSQL_function * func,					 ReturnSetInfo *rsi){	estate->retval = (Datum) 0;	estate->retisnull = true;	estate->rettype = InvalidOid;	estate->fn_rettype = func->fn_rettype;	estate->retistuple = func->fn_retistuple;	estate->retisset = func->fn_retset;	estate->rettupdesc = NULL;	estate->exitlabel = NULL;	estate->tuple_store = NULL;	estate->tuple_store_cxt = NULL;	estate->rsi = rsi;	estate->trig_nargs = 0;	estate->trig_argv = NULL;	estate->found_varno = func->found_varno;	estate->ndatums = func->ndatums;	estate->datums = palloc(sizeof(PLpgSQL_datum *) * estate->ndatums);	/* caller is expected to fill the datums array */	estate->eval_tuptable = NULL;	estate->eval_processed = 0;	estate->eval_lastoid = InvalidOid;	estate->eval_econtext = NULL;	estate->err_func = func;	estate->err_stmt = NULL;	estate->err_text = NULL;}/* ---------- * Release temporary memory used by expression/subselect evaluation * * NB: the result of the evaluation is no longer valid after this is done, * unless it is a pass-by-value datatype. * ---------- */static voidexec_eval_cleanup(PLpgSQL_execstate * estate){	/* Clear result of a full SPI_exec */	if (estate->eval_tuptable != NULL)		SPI_freetuptable(estate->eval_tuptable);	estate->eval_tuptable = NULL;	/* Clear result of exec_eval_simple_expr (but keep the econtext) */	if (estate->eval_econtext != NULL)		ResetExprContext(estate->eval_econtext);}/* ---------- * Generate a prepared plan * ---------- */static voidexec_prepare_plan(PLpgSQL_execstate * estate,				  PLpgSQL_expr * expr){	int			i;	_SPI_plan  *spi_plan;	void	   *plan;	Oid		   *argtypes;	/*	 * We need a temporary argtypes array to load with data. (The finished	 * plan structure will contain a copy of it.)	 *	 * +1 is just to avoid palloc(0) error.	 */	argtypes = (Oid *) palloc(sizeof(Oid) * (expr->nparams + 1));	for (i = 0; i < expr->nparams; i++)	{		Datum		paramval;		bool		paramisnull;		exec_eval_datum(estate, estate->datums[expr->params[i]],						InvalidOid,						&argtypes[i], &paramval, &paramisnull);	}	/*	 * 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);	spi_plan = (_SPI_plan *) expr->plan;	expr->plan_argtypes = spi_plan->argtypes;	expr->expr_simple_expr = NULL;	exec_simple_check_plan(expr);	SPI_freeplan(plan);	pfree(argtypes);}/* ---------- * exec_stmt_execsql			Execute an SQL statement not *					returning any data. * ---------- */static intexec_stmt_execsql(PLpgSQL_execstate * estate,				  PLpgSQL_stmt_execsql * stmt){	int			i;	Datum	   *values;	char	   *nulls;	int			rc;	PLpgSQL_expr *expr = stmt->sqlstmt;	/*	 * 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++)	{		PLpgSQL_datum *datum = estate->datums[expr->params[i]];		Oid			paramtypeid;		bool		paramisnull;		exec_eval_datum(estate, datum, expr->plan_argtypes[i],						&paramtypeid, &values[i], &paramisnull);		if (paramisnull)			nulls[i] = 'n';		else			nulls[i] = ' ';	}	/*	 * Execute the plan	 */	rc = SPI_execp(expr->plan, values, nulls, 0);	switch (rc)	{		case SPI_OK_UTILITY:		case SPI_OK_SELINTO:			break;		case SPI_OK_INSERT:		case SPI_OK_DELETE:		case SPI_OK_UPDATE:			/*			 * If the INSERT, DELETE, or UPDATE query affected at least			 * one tuple, set the magic 'FOUND' variable to true. This			 * conforms with the behavior of PL/SQL.			 */			exec_set_found(estate, (SPI_processed != 0));			break;		case SPI_OK_SELECT:			ereport(ERROR,					(errcode(ERRCODE_SYNTAX_ERROR),			   errmsg("SELECT query has no destination for result data"),					 errhint("If you want to discard the results, use PERFORM instead.")));		default:			elog(ERROR, "error executing query \"%s\"", expr->query);	}	/*	 * Release any result tuples from SPI_execp (probably shouldn't be	 * any)	 */	SPI_freetuptable(SPI_tuptable);	/* Save result info for GET DIAGNOSTICS */	estate->eval_processed = SPI_processed;	estate->eval_lastoid = SPI_lastoid;	pfree(values);	pfree(nulls);	return PLPGSQL_RC_OK;}/* ---------- * exec_stmt_dynexecute			Execute a dynamic SQL query not *					returning any data. * ---------- */static intexec_stmt_dynexecute(PLpgSQL_execstate * estate,					 PLpgSQL_stmt_dynexecute * stmt){	Datum		query;	bool		isnull = false;	Oid			restype;	char	   *querystr;	int			exec_res;	/*	 * First we evaluate the string expression after the EXECUTE keyword.	 * It's result is the querystring we have to execute.	 */	query = exec_eval_expr(estate, stmt->query, &isnull, &restype);	if (isnull)		ereport(ERROR,				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),				 errmsg("cannot EXECUTE a null querystring")));	/* Get the C-String representation */	querystr = convert_value_to_string(query, restype);	exec_eval_cleanup(estate);	/*	 * Call SPI_exec() without preparing a saved plan. The returncode can	 * be any standard OK.	Note that while a SELECT is allowed, its	 * results will be discarded.	 */	exec_res = SPI_exec(querystr, 0);	switch (exec_res)	{		case SPI_OK_SELECT:		case SPI_OK_INSERT:		case SPI_OK_UPDATE:		case SPI_OK_DELETE:		case SPI_OK_UTILITY:			break;		case 0:			/*			 * Also allow a zero return, which implies the querystring

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -