Beispiel #1
0
/* trigger subhandler
 *
 * the python function is expected to return Py_None if the tuple is
 * acceptable and unmodified.  Otherwise it should return a PyString
 * object who's value is SKIP, or MODIFY.  SKIP means don't perform
 * this action.  MODIFY means the tuple has been modified, so update
 * tuple and perform action.  SKIP and MODIFY assume the trigger fires
 * BEFORE the event and is ROW level.  postgres expects the function
 * to take no arguments and return an argument of type trigger.
 */
HeapTuple
PLy_exec_trigger(FunctionCallInfo fcinfo, PLyProcedure *proc)
{
	HeapTuple	rv = NULL;
	PyObject   *volatile plargs = NULL;
	PyObject   *volatile plrv = NULL;
	TriggerData *tdata;

	Assert(CALLED_AS_TRIGGER(fcinfo));

	/*
	 * Input/output conversion for trigger tuples.  Use the result TypeInfo
	 * variable to store the tuple conversion info.  We do this over again on
	 * each call to cover the possibility that the relation's tupdesc changed
	 * since the trigger was last called. PLy_input_tuple_funcs and
	 * PLy_output_tuple_funcs are responsible for not doing repetitive work.
	 */
	tdata = (TriggerData *) fcinfo->context;

	PLy_input_tuple_funcs(&(proc->result), tdata->tg_relation->rd_att);
	PLy_output_tuple_funcs(&(proc->result), tdata->tg_relation->rd_att);

	PG_TRY();
	{
		plargs = PLy_trigger_build_args(fcinfo, proc, &rv);
		plrv = PLy_procedure_call(proc, "TD", plargs);

		Assert(plrv != NULL);

		/*
		 * Disconnect from SPI manager
		 */
		if (SPI_finish() != SPI_OK_FINISH)
			elog(ERROR, "SPI_finish failed");

		/*
		 * return of None means we're happy with the tuple
		 */
		if (plrv != Py_None)
		{
			char	   *srv;

			if (PyString_Check(plrv))
				srv = PyString_AsString(plrv);
			else if (PyUnicode_Check(plrv))
				srv = PLyUnicode_AsString(plrv);
			else
			{
				ereport(ERROR,
						(errcode(ERRCODE_DATA_EXCEPTION),
					errmsg("unexpected return value from trigger procedure"),
						 errdetail("Expected None or a string.")));
				srv = NULL;		/* keep compiler quiet */
			}

			if (pg_strcasecmp(srv, "SKIP") == 0)
				rv = NULL;
			else if (pg_strcasecmp(srv, "MODIFY") == 0)
			{
				TriggerData *tdata = (TriggerData *) fcinfo->context;

				if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event) ||
					TRIGGER_FIRED_BY_UPDATE(tdata->tg_event))
					rv = PLy_modify_tuple(proc, plargs, tdata, rv);
				else
					ereport(WARNING,
							(errmsg("PL/Python trigger function returned \"MODIFY\" in a DELETE trigger -- ignored")));
			}
			else if (pg_strcasecmp(srv, "OK") != 0)
			{
				/*
				 * accept "OK" as an alternative to None; otherwise, raise an
				 * error
				 */
				ereport(ERROR,
						(errcode(ERRCODE_DATA_EXCEPTION),
					errmsg("unexpected return value from trigger procedure"),
						 errdetail("Expected None, \"OK\", \"SKIP\", or \"MODIFY\".")));
			}
		}
	}
	PG_CATCH();
	{
		Py_XDECREF(plargs);
		Py_XDECREF(plrv);

		PG_RE_THROW();
	}
	PG_END_TRY();

	Py_DECREF(plargs);
	Py_DECREF(plrv);

	return rv;
}
Beispiel #2
0
/* function subhandler */
Datum
PLy_exec_function(FunctionCallInfo fcinfo, PLyProcedure *proc)
{
	Datum		rv;
	PyObject   *volatile plargs = NULL;
	PyObject   *volatile plrv = NULL;
	FuncCallContext *volatile funcctx = NULL;
	PLySRFState *volatile srfstate = NULL;
	ErrorContextCallback plerrcontext;

	/*
	 * If the function is called recursively, we must push outer-level
	 * arguments into the stack.  This must be immediately before the PG_TRY
	 * to ensure that the corresponding pop happens.
	 */
	PLy_global_args_push(proc);

	PG_TRY();
	{
		if (proc->is_setof)
		{
			/* First Call setup */
			if (SRF_IS_FIRSTCALL())
			{
				funcctx = SRF_FIRSTCALL_INIT();
				srfstate = (PLySRFState *)
					MemoryContextAllocZero(funcctx->multi_call_memory_ctx,
										   sizeof(PLySRFState));
				/* Immediately register cleanup callback */
				srfstate->callback.func = plpython_srf_cleanup_callback;
				srfstate->callback.arg = (void *) srfstate;
				MemoryContextRegisterResetCallback(funcctx->multi_call_memory_ctx,
												   &srfstate->callback);
				funcctx->user_fctx = (void *) srfstate;
			}
			/* Every call setup */
			funcctx = SRF_PERCALL_SETUP();
			Assert(funcctx != NULL);
			srfstate = (PLySRFState *) funcctx->user_fctx;
		}

		if (srfstate == NULL || srfstate->iter == NULL)
		{
			/*
			 * Non-SETOF function or first time for SETOF function: build
			 * args, then actually execute the function.
			 */
			plargs = PLy_function_build_args(fcinfo, proc);
			plrv = PLy_procedure_call(proc, "args", plargs);
			Assert(plrv != NULL);
		}
		else
		{
			/*
			 * Second or later call for a SETOF function: restore arguments in
			 * globals dict to what they were when we left off.  We must do
			 * this in case multiple evaluations of the same SETOF function
			 * are interleaved.  It's a bit annoying, since the iterator may
			 * not look at the arguments at all, but we have no way to know
			 * that.  Fortunately this isn't terribly expensive.
			 */
			if (srfstate->savedargs)
				PLy_function_restore_args(proc, srfstate->savedargs);
			srfstate->savedargs = NULL; /* deleted by restore_args */
		}

		/*
		 * If it returns a set, call the iterator to get the next return item.
		 * We stay in the SPI context while doing this, because PyIter_Next()
		 * calls back into Python code which might contain SPI calls.
		 */
		if (proc->is_setof)
		{
			if (srfstate->iter == NULL)
			{
				/* first time -- do checks and setup */
				ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;

				if (!rsi || !IsA(rsi, ReturnSetInfo) ||
					(rsi->allowedModes & SFRM_ValuePerCall) == 0)
				{
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("unsupported set function return mode"),
							 errdetail("PL/Python set-returning functions only support returning one value per call.")));
				}
				rsi->returnMode = SFRM_ValuePerCall;

				/* Make iterator out of returned object */
				srfstate->iter = PyObject_GetIter(plrv);

				Py_DECREF(plrv);
				plrv = NULL;

				if (srfstate->iter == NULL)
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("returned object cannot be iterated"),
							 errdetail("PL/Python set-returning functions must return an iterable object.")));
			}

			/* Fetch next from iterator */
			plrv = PyIter_Next(srfstate->iter);
			if (plrv == NULL)
			{
				/* Iterator is exhausted or error happened */
				bool		has_error = (PyErr_Occurred() != NULL);

				Py_DECREF(srfstate->iter);
				srfstate->iter = NULL;

				if (has_error)
					PLy_elog(ERROR, "error fetching next item from iterator");

				/* Pass a null through the data-returning steps below */
				Py_INCREF(Py_None);
				plrv = Py_None;
			}
			else
			{
				/*
				 * This won't be last call, so save argument values.  We do
				 * this again each time in case the iterator is changing those
				 * values.
				 */
				srfstate->savedargs = PLy_function_save_args(proc);
			}
		}

		/*
		 * Disconnect from SPI manager and then create the return values datum
		 * (if the input function does a palloc for it this must not be
		 * allocated in the SPI memory context because SPI_finish would free
		 * it).
		 */
		if (SPI_finish() != SPI_OK_FINISH)
			elog(ERROR, "SPI_finish failed");

		plerrcontext.callback = plpython_return_error_callback;
		plerrcontext.previous = error_context_stack;
		error_context_stack = &plerrcontext;

		/*
		 * If the function is declared to return void, the Python return value
		 * must be None. For void-returning functions, we also treat a None
		 * return value as a special "void datum" rather than NULL (as is the
		 * case for non-void-returning functions).
		 */
		if (proc->result.out.d.typoid == VOIDOID)
		{
			if (plrv != Py_None)
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg("PL/Python function with return type \"void\" did not return None")));

			fcinfo->isnull = false;
			rv = (Datum) 0;
		}
		else if (plrv == Py_None)
		{
			fcinfo->isnull = true;

			/*
			 * In a SETOF function, the iteration-ending null isn't a real
			 * value; don't pass it through the input function, which might
			 * complain.
			 */
			if (srfstate && srfstate->iter == NULL)
				rv = (Datum) 0;
			else if (proc->result.is_rowtype < 1)
				rv = InputFunctionCall(&proc->result.out.d.typfunc,
									   NULL,
									   proc->result.out.d.typioparam,
									   -1);
			else
				/* Tuple as None */
				rv = (Datum) NULL;
		}
		else if (proc->result.is_rowtype >= 1)
		{
			TupleDesc	desc;

			/* make sure it's not an unnamed record */
			Assert((proc->result.out.d.typoid == RECORDOID &&
					proc->result.out.d.typmod != -1) ||
				   (proc->result.out.d.typoid != RECORDOID &&
					proc->result.out.d.typmod == -1));

			desc = lookup_rowtype_tupdesc(proc->result.out.d.typoid,
										  proc->result.out.d.typmod);

			rv = PLyObject_ToCompositeDatum(&proc->result, desc, plrv);
			fcinfo->isnull = (rv == (Datum) NULL);

			ReleaseTupleDesc(desc);
		}
		else
		{
			fcinfo->isnull = false;
			rv = (proc->result.out.d.func) (&proc->result.out.d, -1, plrv);
		}
	}
	PG_CATCH();
	{
		/* Pop old arguments from the stack if they were pushed above */
		PLy_global_args_pop(proc);

		Py_XDECREF(plargs);
		Py_XDECREF(plrv);

		/*
		 * If there was an error within a SRF, the iterator might not have
		 * been exhausted yet.  Clear it so the next invocation of the
		 * function will start the iteration again.  (This code is probably
		 * unnecessary now; plpython_srf_cleanup_callback should take care of
		 * cleanup.  But it doesn't hurt anything to do it here.)
		 */
		if (srfstate)
		{
			Py_XDECREF(srfstate->iter);
			srfstate->iter = NULL;
			/* And drop any saved args; we won't need them */
			if (srfstate->savedargs)
				PLy_function_drop_args(srfstate->savedargs);
			srfstate->savedargs = NULL;
		}

		PG_RE_THROW();
	}
	PG_END_TRY();

	error_context_stack = plerrcontext.previous;

	/* Pop old arguments from the stack if they were pushed above */
	PLy_global_args_pop(proc);

	Py_XDECREF(plargs);
	Py_DECREF(plrv);

	if (srfstate)
	{
		/* We're in a SRF, exit appropriately */
		if (srfstate->iter == NULL)
		{
			/* Iterator exhausted, so we're done */
			SRF_RETURN_DONE(funcctx);
		}
		else if (fcinfo->isnull)
			SRF_RETURN_NEXT_NULL(funcctx);
		else
			SRF_RETURN_NEXT(funcctx, rv);
	}

	/* Plain function, just return the Datum value (possibly null) */
	return rv;
}
Beispiel #3
0
/* function subhandler */
Datum
PLy_exec_function(FunctionCallInfo fcinfo, PLyProcedure *proc)
{
	Datum		rv;
	PyObject   *volatile plargs = NULL;
	PyObject   *volatile plrv = NULL;
	ErrorContextCallback plerrcontext;

	PG_TRY();
	{
		if (!proc->is_setof || proc->setof == NULL)
		{
			/*
			 * Simple type returning function or first time for SETOF
			 * function: actually execute the function.
			 */
			plargs = PLy_function_build_args(fcinfo, proc);
			plrv = PLy_procedure_call(proc, "args", plargs);
			if (!proc->is_setof)
			{
				/*
				 * SETOF function parameters will be deleted when last row is
				 * returned
				 */
				PLy_function_delete_args(proc);
			}
			Assert(plrv != NULL);
		}

		/*
		 * If it returns a set, call the iterator to get the next return item.
		 * We stay in the SPI context while doing this, because PyIter_Next()
		 * calls back into Python code which might contain SPI calls.
		 */
		if (proc->is_setof)
		{
			bool		has_error = false;
			ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;

			if (proc->setof == NULL)
			{
				/* first time -- do checks and setup */
				if (!rsi || !IsA(rsi, ReturnSetInfo) ||
					(rsi->allowedModes & SFRM_ValuePerCall) == 0)
				{
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("unsupported set function return mode"),
							 errdetail("PL/Python set-returning functions only support returning only value per call.")));
				}
				rsi->returnMode = SFRM_ValuePerCall;

				/* Make iterator out of returned object */
				proc->setof = PyObject_GetIter(plrv);
				Py_DECREF(plrv);
				plrv = NULL;

				if (proc->setof == NULL)
					ereport(ERROR,
							(errcode(ERRCODE_DATATYPE_MISMATCH),
							 errmsg("returned object cannot be iterated"),
							 errdetail("PL/Python set-returning functions must return an iterable object.")));
			}

			/* Fetch next from iterator */
			plrv = PyIter_Next(proc->setof);
			if (plrv)
				rsi->isDone = ExprMultipleResult;
			else
			{
				rsi->isDone = ExprEndResult;
				has_error = PyErr_Occurred() != NULL;
			}

			if (rsi->isDone == ExprEndResult)
			{
				/* Iterator is exhausted or error happened */
				Py_DECREF(proc->setof);
				proc->setof = NULL;

				Py_XDECREF(plargs);
				Py_XDECREF(plrv);

				PLy_function_delete_args(proc);

				if (has_error)
					PLy_elog(ERROR, "error fetching next item from iterator");

				/* Disconnect from the SPI manager before returning */
				if (SPI_finish() != SPI_OK_FINISH)
					elog(ERROR, "SPI_finish failed");

				fcinfo->isnull = true;
				return (Datum) NULL;
			}
		}

		/*
		 * Disconnect from SPI manager and then create the return values datum
		 * (if the input function does a palloc for it this must not be
		 * allocated in the SPI memory context because SPI_finish would free
		 * it).
		 */
		if (SPI_finish() != SPI_OK_FINISH)
			elog(ERROR, "SPI_finish failed");

		plerrcontext.callback = plpython_return_error_callback;
		plerrcontext.previous = error_context_stack;
		error_context_stack = &plerrcontext;

		/*
		 * If the function is declared to return void, the Python return value
		 * must be None. For void-returning functions, we also treat a None
		 * return value as a special "void datum" rather than NULL (as is the
		 * case for non-void-returning functions).
		 */
		if (proc->result.out.d.typoid == VOIDOID)
		{
			if (plrv != Py_None)
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg("PL/Python function with return type \"void\" did not return None")));

			fcinfo->isnull = false;
			rv = (Datum) 0;
		}
		else if (plrv == Py_None)
		{
			fcinfo->isnull = true;
			if (proc->result.is_rowtype < 1)
				rv = InputFunctionCall(&proc->result.out.d.typfunc,
									   NULL,
									   proc->result.out.d.typioparam,
									   -1);
			else
				/* Tuple as None */
				rv = (Datum) NULL;
		}
		else if (proc->result.is_rowtype >= 1)
		{
			TupleDesc	desc;

			/* make sure it's not an unnamed record */
			Assert((proc->result.out.d.typoid == RECORDOID &&
					proc->result.out.d.typmod != -1) ||
				   (proc->result.out.d.typoid != RECORDOID &&
					proc->result.out.d.typmod == -1));

			desc = lookup_rowtype_tupdesc(proc->result.out.d.typoid,
										  proc->result.out.d.typmod);

			rv = PLyObject_ToCompositeDatum(&proc->result, desc, plrv);
			fcinfo->isnull = (rv == (Datum) NULL);
		}
		else
		{
			fcinfo->isnull = false;
			rv = (proc->result.out.d.func) (&proc->result.out.d, -1, plrv);
		}
	}
	PG_CATCH();
	{
		Py_XDECREF(plargs);
		Py_XDECREF(plrv);

		/*
		 * If there was an error the iterator might have not been exhausted
		 * yet. Set it to NULL so the next invocation of the function will
		 * start the iteration again.
		 */
		Py_XDECREF(proc->setof);
		proc->setof = NULL;

		PG_RE_THROW();
	}
	PG_END_TRY();

	error_context_stack = plerrcontext.previous;

	Py_XDECREF(plargs);
	Py_DECREF(plrv);

	return rv;
}