示例#1
0
Datum Type_invokeSRF(Type self, jclass cls, jmethodID method, jvalue* args, PG_FUNCTION_ARGS)
{
	bool hasRow;
	CallContextData* ctxData;
	FuncCallContext* context;
	MemoryContext currCtx;

	/* stuff done only on the first call of the function
	 */
	if(SRF_IS_FIRSTCALL())
	{
		jobject tmp;

		/* create a function context for cross-call persistence
		 */
		context = SRF_FIRSTCALL_INIT();
		currCtx = MemoryContextSwitchTo(context->multi_call_memory_ctx);

		/* Call the declared Java function. It returns an instance that can produce
		 * the rows.
		 */
		tmp = Type_getSRFProducer(self, cls, method, args);
		if(tmp == 0)
		{
			Invocation_assertDisconnect();
			MemoryContextSwitchTo(currCtx);
			fcinfo->isnull = true;
			SRF_RETURN_DONE(context);
		}

		ctxData = (CallContextData*)palloc(sizeof(CallContextData));
		context->user_fctx = ctxData;

		ctxData->elemType = self;
		ctxData->rowProducer = JNI_newGlobalRef(tmp);
		JNI_deleteLocalRef(tmp);

		/* Some row producers will need a writable result set in order
		 * to produce the row. If one is needed, it's created here.
		 */
		tmp = Type_getSRFCollector(self, fcinfo);
		if(tmp == 0)
			ctxData->rowCollector = 0;
		else
		{
			ctxData->rowCollector = JNI_newGlobalRef(tmp);
			JNI_deleteLocalRef(tmp);
		}		

		ctxData->trusted       = currentInvocation->trusted;
		ctxData->hasConnected  = currentInvocation->hasConnected;
		ctxData->invocation    = currentInvocation->invocation;
		if(ctxData->hasConnected)
			ctxData->spiContext = CurrentMemoryContext;
		else
			ctxData->spiContext = 0;

		ctxData->rowContext = AllocSetContextCreate(context->multi_call_memory_ctx,
								  "PL/Java row context",
								  ALLOCSET_DEFAULT_MINSIZE,
								  ALLOCSET_DEFAULT_INITSIZE,
								  ALLOCSET_DEFAULT_MAXSIZE);

		/* Register callback to be called when the function ends
		 */
		RegisterExprContextCallback(((ReturnSetInfo*)fcinfo->resultinfo)->econtext, _endOfSetCB, PointerGetDatum(ctxData));
		MemoryContextSwitchTo(currCtx);
	}

	context = SRF_PERCALL_SETUP();
	ctxData = (CallContextData*)context->user_fctx;
	MemoryContextReset(ctxData->rowContext);
	currCtx = MemoryContextSwitchTo(ctxData->rowContext);
	currentInvocation->hasConnected = ctxData->hasConnected;
	currentInvocation->invocation   = ctxData->invocation;

	hasRow = Type_hasNextSRF(self, ctxData->rowProducer, ctxData->rowCollector, (jint)context->call_cntr);

	ctxData->hasConnected = currentInvocation->hasConnected;
	ctxData->invocation   = currentInvocation->invocation;
	currentInvocation->hasConnected = false;
	currentInvocation->invocation   = 0;

	if(hasRow)
	{
		Datum result = Type_nextSRF(self, ctxData->rowProducer, ctxData->rowCollector);
		MemoryContextSwitchTo(currCtx);
		SRF_RETURN_NEXT(context, result);
	}

	MemoryContextSwitchTo(currCtx);

	/* Unregister this callback and call it manually. We do this because
	 * otherwise it will be called when the backend is in progress of
	 * cleaning up Portals. If we close cursors (i.e. drop portals) in
	 * the close, then that mechanism fails since attempts are made to
	 * delete portals more then once.
	 */
	UnregisterExprContextCallback(
		((ReturnSetInfo*)fcinfo->resultinfo)->econtext,
		_endOfSetCB,
		PointerGetDatum(ctxData));

	_closeIteration(ctxData);

	/* This is the end of the set.
	 */
	SRF_RETURN_DONE(context);
}
示例#2
0
文件: execSRF.c 项目: Brar/postgres
/*
 *		ExecMakeFunctionResultSet
 *
 * Evaluate the arguments to a set-returning function and then call the
 * function itself.  The argument expressions may not contain set-returning
 * functions (the planner is supposed to have separated evaluation for those).
 *
 * This should be called in a short-lived (per-tuple) context, argContext
 * needs to live until all rows have been returned (i.e. *isDone set to
 * ExprEndResult or ExprSingleResult).
 *
 * This is used by nodeProjectSet.c.
 */
Datum
ExecMakeFunctionResultSet(SetExprState *fcache,
						  ExprContext *econtext,
						  MemoryContext argContext,
						  bool *isNull,
						  ExprDoneCond *isDone)
{
	List	   *arguments;
	Datum		result;
	FunctionCallInfo fcinfo;
	PgStat_FunctionCallUsage fcusage;
	ReturnSetInfo rsinfo;
	bool		callit;
	int			i;

restart:

	/* Guard against stack overflow due to overly complex expressions */
	check_stack_depth();

	/*
	 * If a previous call of the function returned a set result in the form of
	 * a tuplestore, continue reading rows from the tuplestore until it's
	 * empty.
	 */
	if (fcache->funcResultStore)
	{
		TupleTableSlot *slot = fcache->funcResultSlot;
		MemoryContext oldContext;
		bool		foundTup;

		/*
		 * Have to make sure tuple in slot lives long enough, otherwise
		 * clearing the slot could end up trying to free something already
		 * freed.
		 */
		oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
		foundTup = tuplestore_gettupleslot(fcache->funcResultStore, true, false,
										   fcache->funcResultSlot);
		MemoryContextSwitchTo(oldContext);

		if (foundTup)
		{
			*isDone = ExprMultipleResult;
			if (fcache->funcReturnsTuple)
			{
				/* We must return the whole tuple as a Datum. */
				*isNull = false;
				return ExecFetchSlotTupleDatum(fcache->funcResultSlot);
			}
			else
			{
				/* Extract the first column and return it as a scalar. */
				return slot_getattr(fcache->funcResultSlot, 1, isNull);
			}
		}
		/* Exhausted the tuplestore, so clean up */
		tuplestore_end(fcache->funcResultStore);
		fcache->funcResultStore = NULL;
		*isDone = ExprEndResult;
		*isNull = true;
		return (Datum) 0;
	}

	/*
	 * arguments is a list of expressions to evaluate before passing to the
	 * function manager.  We skip the evaluation if it was already done in the
	 * previous call (ie, we are continuing the evaluation of a set-valued
	 * function).  Otherwise, collect the current argument values into fcinfo.
	 *
	 * The arguments have to live in a context that lives at least until all
	 * rows from this SRF have been returned, otherwise ValuePerCall SRFs
	 * would reference freed memory after the first returned row.
	 */
	fcinfo = &fcache->fcinfo_data;
	arguments = fcache->args;
	if (!fcache->setArgsValid)
	{
		MemoryContext oldContext = MemoryContextSwitchTo(argContext);

		ExecEvalFuncArgs(fcinfo, arguments, econtext);
		MemoryContextSwitchTo(oldContext);
	}
	else
	{
		/* Reset flag (we may set it again below) */
		fcache->setArgsValid = false;
	}

	/*
	 * Now call the function, passing the evaluated parameter values.
	 */

	/* Prepare a resultinfo node for communication. */
	fcinfo->resultinfo = (Node *) &rsinfo;
	rsinfo.type = T_ReturnSetInfo;
	rsinfo.econtext = econtext;
	rsinfo.expectedDesc = fcache->funcResultDesc;
	rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize);
	/* note we do not set SFRM_Materialize_Random or _Preferred */
	rsinfo.returnMode = SFRM_ValuePerCall;
	/* isDone is filled below */
	rsinfo.setResult = NULL;
	rsinfo.setDesc = NULL;

	/*
	 * If function is strict, and there are any NULL arguments, skip calling
	 * the function.
	 */
	callit = true;
	if (fcache->func.fn_strict)
	{
		for (i = 0; i < fcinfo->nargs; i++)
		{
			if (fcinfo->argnull[i])
			{
				callit = false;
				break;
			}
		}
	}

	if (callit)
	{
		pgstat_init_function_usage(fcinfo, &fcusage);

		fcinfo->isnull = false;
		rsinfo.isDone = ExprSingleResult;
		result = FunctionCallInvoke(fcinfo);
		*isNull = fcinfo->isnull;
		*isDone = rsinfo.isDone;

		pgstat_end_function_usage(&fcusage,
								  rsinfo.isDone != ExprMultipleResult);
	}
	else
	{
		/* for a strict SRF, result for NULL is an empty set */
		result = (Datum) 0;
		*isNull = true;
		*isDone = ExprEndResult;
	}

	/* Which protocol does function want to use? */
	if (rsinfo.returnMode == SFRM_ValuePerCall)
	{
		if (*isDone != ExprEndResult)
		{
			/*
			 * Save the current argument values to re-use on the next call.
			 */
			if (*isDone == ExprMultipleResult)
			{
				fcache->setArgsValid = true;
				/* Register cleanup callback if we didn't already */
				if (!fcache->shutdown_reg)
				{
					RegisterExprContextCallback(econtext,
												ShutdownSetExpr,
												PointerGetDatum(fcache));
					fcache->shutdown_reg = true;
				}
			}
		}
	}
	else if (rsinfo.returnMode == SFRM_Materialize)
	{
		/* check we're on the same page as the function author */
		if (rsinfo.isDone != ExprSingleResult)
			ereport(ERROR,
					(errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
					 errmsg("table-function protocol for materialize mode was not followed")));
		if (rsinfo.setResult != NULL)
		{
			/* prepare to return values from the tuplestore */
			ExecPrepareTuplestoreResult(fcache, econtext,
										rsinfo.setResult,
										rsinfo.setDesc);
			/* loop back to top to start returning from tuplestore */
			goto restart;
		}
		/* if setResult was left null, treat it as empty set */
		*isDone = ExprEndResult;
		*isNull = true;
		result = (Datum) 0;
	}
	else
		ereport(ERROR,
				(errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
				 errmsg("unrecognized table-function returnMode: %d",
						(int) rsinfo.returnMode)));

	return result;
}
示例#3
0
文件: funcapi.c 项目: mjw56/postgres
/*
 * init_MultiFuncCall
 * Create an empty FuncCallContext data structure
 * and do some other basic Multi-function call setup
 * and error checking
 */
FuncCallContext *
init_MultiFuncCall(PG_FUNCTION_ARGS)
{
	FuncCallContext *retval;

	/*
	 * Bail if we're called in the wrong context
	 */
	if (fcinfo->resultinfo == NULL || !IsA(fcinfo->resultinfo, ReturnSetInfo))
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("set-valued function called in context that cannot accept a set")));

	if (fcinfo->flinfo->fn_extra == NULL)
	{
		/*
		 * First call
		 */
		ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
		MemoryContext multi_call_ctx;

		/*
		 * Create a suitably long-lived context to hold cross-call data
		 */
		multi_call_ctx = AllocSetContextCreate(fcinfo->flinfo->fn_mcxt,
											   "SRF multi-call context",
											   ALLOCSET_SMALL_MINSIZE,
											   ALLOCSET_SMALL_INITSIZE,
											   ALLOCSET_SMALL_MAXSIZE);

		/*
		 * Allocate suitably long-lived space and zero it
		 */
		retval = (FuncCallContext *)
			MemoryContextAllocZero(multi_call_ctx,
								   sizeof(FuncCallContext));

		/*
		 * initialize the elements
		 */
		retval->call_cntr = 0;
		retval->max_calls = 0;
		retval->slot = NULL;
		retval->user_fctx = NULL;
		retval->attinmeta = NULL;
		retval->tuple_desc = NULL;
		retval->multi_call_memory_ctx = multi_call_ctx;

		/*
		 * save the pointer for cross-call use
		 */
		fcinfo->flinfo->fn_extra = retval;

		/*
		 * Ensure we will get shut down cleanly if the exprcontext is not run
		 * to completion.
		 */
		RegisterExprContextCallback(rsi->econtext,
									shutdown_MultiFuncCall,
									PointerGetDatum(fcinfo->flinfo));
	}
	else
	{
		/* second and subsequent calls */
		elog(ERROR, "init_MultiFuncCall cannot be called more than once");

		/* never reached, but keep compiler happy */
		retval = NULL;
	}

	return retval;
}
示例#4
0
/*
 * gp_persistent_relation_node_check()
 *
 * Reads the physical filesystem for every defined filespace and returns the
 * list of relfilenodes that actually exist.  This list should match the set of
 * relfilenodes tracked in gp_persistent_relation_node.
 */
Datum
gp_persistent_relation_node_check(PG_FUNCTION_ARGS)
{
	FuncCallContext     *fcontext;
	node_check_data     *fdata;
	ReturnSetInfo       *rsinfo;
	MemoryContext        oldcontext;
	Oid                  relfilenode = InvalidOid;
	int32				 segnum		 = 0;
	HeapTuple			 tuple;
	char				*primaryPath = NULL;
	char				*mirrorPath  = NULL;

	if (SRF_IS_FIRSTCALL())
	{
		Relation    rel;
		TupleDesc	tupdesc;

		fcontext = SRF_FIRSTCALL_INIT();

		rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;

		/*
		 * The fdata cannot be allocated in the multi_call_ctx because the
		 * multi_call_context gets cleaned up by the MultiFuncCall callback
		 * function which gets called before the callback this function
		 * registers to cleanup the fdata structure.  So instead we allocate
		 * in the parent context fn_mcxt.
		 */
		oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
		fdata = (node_check_data*) palloc0(sizeof(node_check_data));
		fcontext->user_fctx = fdata;

		/* 
		 * Register a call to cleanup when the function ends.
		 */
		RegisterExprContextCallback(rsinfo->econtext, nodeCheckCleanup,
									PointerGetDatum(fdata));

		/* 
		 * Setup the main loop over the list of tablespaces 
		 */
		fdata->tablespaceRelation = 
			heap_open(TableSpaceRelationId, AccessShareLock);
		fdata->scandesc = 
			heap_beginscan(fdata->tablespaceRelation, SnapshotNow, 0, NULL);

		/*
		 * Bless a tuple descriptor for the return type
		 */
		MemoryContextSwitchTo(fcontext->multi_call_memory_ctx);
		rel = RelationIdGetRelation(GpPersistentRelationNodeRelationId);
		tupdesc = RelationGetDescr(rel);
		fcontext->tuple_desc = BlessTupleDesc(tupdesc);
		relation_close(rel, NoLock);

		MemoryContextSwitchTo(oldcontext);
	}

	fcontext = SRF_PERCALL_SETUP();
	fdata = fcontext->user_fctx;

	/*
	 * The code here is basically a nested loop that has been unwound so that
	 * it can be wrapped up into a set-returning function.
	 *
	 * Basic structure is:
	 *  - Loop over tablespace relation
	 *    - Loop over database directories in the tablespace
	 *      - Loop over relfilenodes in the directory
	 *        - Return each tablespace, database, relfilenode, segment_number.
	 *
	 * The complicating factor is that we return from this function and
	 * reenter the loop at the innermost level, so the entire loop is turned
	 * inside-out.
	 */
	while (true)
	{

		/* Innermost loop */
		if (fdata->databaseDir)
		{
			struct dirent		*dent;
			Datum				 values[Natts_gp_persistent_relation_node];
			bool				 nulls[Natts_gp_persistent_relation_node];
			
			dent = ReadDir(fdata->databaseDir, fdata->databaseDirName);
			if (!dent)
			{  /* step out of innermost loop */
				FreeDir(fdata->databaseDir);
				fdata->databaseDir = NULL;
				continue;  
			}
			
			/* skip the boring stuff */
			if (strcmp(dent->d_name, ".") == 0 || 
				strcmp(dent->d_name, "..") == 0)
				continue;
			
			/* Skip things that don't look like relfilenodes */
			if (!strToRelfilenode(dent->d_name, &relfilenode, &segnum))
				continue;

			/* Return relfilenodes as we find them */
			MemSet(nulls, true, sizeof(nulls));
			nulls[Anum_gp_persistent_relation_node_tablespace_oid-1]   = false;
			nulls[Anum_gp_persistent_relation_node_database_oid-1]     = false;
			nulls[Anum_gp_persistent_relation_node_relfilenode_oid-1]  = false;
			nulls[Anum_gp_persistent_relation_node_segment_file_num-1] = false;
			values[Anum_gp_persistent_relation_node_tablespace_oid-1] =
				ObjectIdGetDatum(fdata->tablespaceOid);
			values[Anum_gp_persistent_relation_node_database_oid-1] =
				ObjectIdGetDatum(fdata->databaseOid);
			values[Anum_gp_persistent_relation_node_relfilenode_oid-1] =
				ObjectIdGetDatum(relfilenode);
			values[Anum_gp_persistent_relation_node_segment_file_num-1] =
				Int32GetDatum(segnum);

			tuple = heap_form_tuple(fcontext->tuple_desc, values, nulls);
			
			SRF_RETURN_NEXT(fcontext, HeapTupleGetDatum(tuple));
		}

		/* Loop over database directories in the tablespace */
		if (fdata->tablespaceDir)
		{
			struct dirent *dent;

			dent = ReadDir(fdata->tablespaceDir, fdata->tablespaceDirName);
			if (!dent)
			{  /* step out of database loop */
				FreeDir(fdata->tablespaceDir);
				fdata->tablespaceDir = NULL;
				continue;  
			}
			
			/* skip the borring stuff */
			if (strcmp(dent->d_name, ".") == 0 || 
				strcmp(dent->d_name, "..") == 0)
				continue;
			
			/* Skip things that don't look like database oids */
			if (strlen(dent->d_name) != strspn(dent->d_name, "0123456789"))
				continue;

			/* convert the string to an oid */
			fdata->databaseOid = pg_atoi(dent->d_name, 4, 0);
			
			/* form a database path using this oid */
			snprintf(fdata->databaseDirName, MAXPGPATH, "%s/%s",
					 fdata->tablespaceDirName, 
					 dent->d_name);
			
			oldcontext = 
				MemoryContextSwitchTo(fcontext->multi_call_memory_ctx);
			fdata->databaseDir = AllocateDir(fdata->databaseDirName);
			MemoryContextSwitchTo(oldcontext);

			if (fdata->databaseDir == NULL)
				ereport(ERROR,
						(errcode_for_file_access(),
						 errmsg("could not open directory \"%s\": %m",
								fdata->databaseDirName)));
			continue;
		}

		/* Outermost loop over tablespaces */
		tuple = heap_getnext(fdata->scandesc, ForwardScanDirection);
		if (!HeapTupleIsValid(tuple))
			SRF_RETURN_DONE(fcontext);  /* FINAL return */
				
		fdata->tablespaceOid = HeapTupleGetOid(tuple);
				
		PersistentTablespace_GetPrimaryAndMirrorFilespaces(
			fdata->tablespaceOid, &primaryPath, &mirrorPath);

		/* Find the location of this tablespace on disk */
		FormTablespacePath(fdata->tablespaceDirName, 
						   primaryPath, fdata->tablespaceOid);

		/* 
		 * Primary path is null for the pg_system filespace, additionally
		 * Mirror path is null if there are no mirrors 
		 */
		if (primaryPath)
		{
			pfree(primaryPath);
			primaryPath = NULL;
		}
		if (mirrorPath)
		{
			pfree(mirrorPath);
			mirrorPath = NULL;
		}
				
		oldcontext = 
			MemoryContextSwitchTo(fcontext->multi_call_memory_ctx);
		fdata->tablespaceDir = AllocateDir(fdata->tablespaceDirName);
		MemoryContextSwitchTo(oldcontext);

		if (fdata->tablespaceDir == NULL)
			ereport(ERROR,
					(errcode_for_file_access(),
					 errmsg("could not open directory \"%s\": %m",
							fdata->tablespaceDirName)));
			
		/* The global tablespace doesn't have database directories */
		if (fdata->tablespaceOid == GLOBALTABLESPACE_OID)
		{
			fdata->databaseOid = 0;
			
			/* Skip to the innermost loop */
			fdata->databaseDir = fdata->tablespaceDir;
			fdata->tablespaceDir = NULL;
			strncpy(fdata->databaseDirName, fdata->tablespaceDirName, 
					MAXPGPATH);
		}
	}

	/* Unreachable */
	SRF_RETURN_DONE(fcontext);
}