pl_exec.c

来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 2,523 行 · 第 1/5 页

C
2,523
字号
		}	}	return PLPGSQL_RC_OK;}/* ---------- * exec_stmt_fori			Iterate an integer variable *					from a lower to an upper value *					incrementing or decrementing by the BY value * ---------- */static intexec_stmt_fori(PLpgSQL_execstate *estate, PLpgSQL_stmt_fori *stmt){	PLpgSQL_var *var;	Datum		value;	bool		isnull;	Oid			valtype;	int32		loop_value;	int32		end_value;	int32		step_value;	bool		found = false;	int			rc = PLPGSQL_RC_OK;	var = (PLpgSQL_var *) (estate->datums[stmt->var->varno]);	/*	 * Get the value of the lower bound	 */	value = exec_eval_expr(estate, stmt->lower, &isnull, &valtype);	value = exec_cast_value(value, valtype, var->datatype->typoid,							&(var->datatype->typinput),							var->datatype->typioparam,							var->datatype->atttypmod, isnull);	if (isnull)		ereport(ERROR,				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),				 errmsg("lower bound of FOR loop cannot be NULL")));	loop_value = DatumGetInt32(value);	exec_eval_cleanup(estate);	/*	 * Get the value of the upper bound	 */	value = exec_eval_expr(estate, stmt->upper, &isnull, &valtype);	value = exec_cast_value(value, valtype, var->datatype->typoid,							&(var->datatype->typinput),							var->datatype->typioparam,							var->datatype->atttypmod, isnull);	if (isnull)		ereport(ERROR,				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),				 errmsg("upper bound of FOR loop cannot be NULL")));	end_value = DatumGetInt32(value);	exec_eval_cleanup(estate);	/*	 * Get the step value	 */	if (stmt->step)	{		value = exec_eval_expr(estate, stmt->step, &isnull, &valtype);		value = exec_cast_value(value, valtype, var->datatype->typoid,								&(var->datatype->typinput),								var->datatype->typioparam,								var->datatype->atttypmod, isnull);		if (isnull)			ereport(ERROR,					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),					 errmsg("BY value of FOR loop cannot be NULL")));		step_value = DatumGetInt32(value);		exec_eval_cleanup(estate);		if (step_value <= 0)			ereport(ERROR,					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),				  errmsg("BY value of FOR loop must be greater than zero")));	}	else		step_value = 1;	/*	 * Now do the loop	 */	for (;;)	{		/*		 * Check against upper bound		 */		if (stmt->reverse)		{			if (loop_value < end_value)				break;		}		else		{			if (loop_value > end_value)				break;		}		found = true;			/* looped at least once */		/*		 * Assign current value to loop var		 */		var->value = Int32GetDatum(loop_value);		var->isnull = false;		/*		 * Execute the statements		 */		rc = exec_stmts(estate, stmt->body);		if (rc == PLPGSQL_RC_RETURN)			break;				/* return from function */		else 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, this is a labelled exit that does not match the			 * current statement's label, if any: return RC_EXIT so that the			 * EXIT continues to propagate up the stack.			 */			break;		}		else if (rc == PLPGSQL_RC_CONTINUE)		{			if (estate->exitlabel == NULL)				/* unlabelled continue, so re-run the current loop */				rc = PLPGSQL_RC_OK;			else if (stmt->label != NULL &&					 strcmp(stmt->label, estate->exitlabel) == 0)			{				/* label matches named continue, so re-run loop */				estate->exitlabel = NULL;				rc = PLPGSQL_RC_OK;			}			else			{				/*				 * otherwise, this is 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.				 */				break;			}		}		/*		 * Increase/decrease loop value, unless it would overflow, in which		 * case exit the loop.		 */		if (stmt->reverse)		{			if ((int32) (loop_value - step_value) > loop_value)				break;			loop_value -= step_value;		}		else		{			if ((int32) (loop_value + step_value) < loop_value)				break;			loop_value += step_value;		}	}	/*	 * 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_fors			Execute a query, assign each *					tuple to a record or row and *					execute a group of statements *					for it. * ---------- */static intexec_stmt_fors(PLpgSQL_execstate *estate, PLpgSQL_stmt_fors *stmt){	PLpgSQL_rec *rec = NULL;	PLpgSQL_row *row = NULL;	SPITupleTable *tuptab;	Portal		portal;	bool		found = false;	int			rc = PLPGSQL_RC_OK;	int			i;	int			n;	/*	 * 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_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)

⌨️ 快捷键说明

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