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

📄 pl_exec.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 5 页
字号:
	 * 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_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;	uint32		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");	/*	 * Run the query	 */	exec_run_select(estate, stmt->query, 1, NULL);	tuptab = estate->eval_tuptable;	n = estate->eval_processed;	/*	 * If the query didn't return any rows, set the target to NULL and return.	 */	if (n == 0)	{		exec_move_row(estate, rec, row, NULL, tuptab->tupdesc);		exec_eval_cleanup(estate);		return PLPGSQL_RC_OK;	}	/*	 * Put the result into the target and set found to true	 */	exec_move_row(estate, rec, row, tuptab->vals[0], tuptab->tupdesc);	exec_set_found(estate, true);	exec_eval_cleanup(estate);	return PLPGSQL_RC_OK;}/* ---------- * 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)		exec_init_tuple_store(estate);	/* rettupdesc will be filled by exec_init_tuple_store */	tupdesc = estate->rettupdesc;	natts = tupdesc->natts;	if (stmt->retvarno >= 0)	{		PLpgSQL_datum *retvar = estate->datums[stmt->retvarno];		switch (retvar->dtype)		{			case PLPGSQL_DTYPE_VAR:				{					PLpgSQL_var *var = (PLpgSQL_var *) retvar;					Datum		retval = var->value;					bool		isNull = var->isnull;					if (natts != 1)						ereport(ERROR,								(errcode(ERRCODE_DATATYPE_MISMATCH),						errmsg("wrong result type supplied in RETURN NEXT")));					/* coerce type if needed */					retval = exec_simple_cast_value(retval,													var->datatype->typoid,												 tupdesc->attrs[0]->atttypid,												tupdesc->attrs[0]->atttypmod,													isNull);					tuple = heap_form_tuple(tupdesc, &retval, &isNull);					free_tuple = true;				}				break;			case PLPGSQL_DTYPE_REC:				{					PLpgSQL_rec *rec = (PLpgSQL_rec *) retvar;					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;				}				break;			case PLPGSQL_DTYPE_ROW:				{					PLpgSQL_row *row = (PLpgSQL_row *) retvar;					tuple = make_tuple_from_row(estate, row, tupdesc);					if (tuple == NULL)						ereport(ERROR,								(errcode(ERRCODE_DATATYPE_MISMATCH),						errmsg("wrong record type supplied in RETURN NEXT")));					free_tuple = true;				}				break;			default:				elog(ERROR, "unrecognized dtype: %d", retvar->dtype);				tuple = NULL;	/* keep compiler quiet */				break;		}	}	else if (stmt->expr)	{		Datum		retval;		bool		isNull;		Oid			rettype;		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);		tuple = heap_form_tuple(tupdesc, &retval, &isNull);		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, work_mem);	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){	char	   *cp;

⌨️ 快捷键说明

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