blob: 5587b25ebf48d5f93d58000ef55ab65110e82c6f [file] [log] [blame]
/* ----------------------------------------------------------------------- *//**
*
* @file FunctionHandle_impl.hpp
*
*//* ----------------------------------------------------------------------- */
#ifndef MADLIB_POSTGRES_FUNCTIONHANDLE_IMPL_HPP
#define MADLIB_POSTGRES_FUNCTIONHANDLE_IMPL_HPP
namespace madlib {
namespace dbconnector {
namespace postgres {
inline
FunctionHandle::Argument::Argument(AnyType inValue)
: AnyType(inValue) { }
template <typename T>
inline
FunctionHandle::Argument::Argument(const T& inValue)
: AnyType(inValue, true /* forceLazyConversionToDatum */) { }
inline
FunctionHandle::FunctionHandle(SystemInformation* inSysInfo, Oid inFuncID)
: mSysInfo(inSysInfo),
mFuncInfo(inSysInfo->functionInformation(inFuncID)),
mFuncCallOptions(GarbageCollectionAfterCall) { }
inline
UDF::Pointer
FunctionHandle::funcPtr() {
return mFuncInfo->cxx_func;
}
/**
* @brief Return the OID of this function
*/
inline
Oid
FunctionHandle::funcID() const {
return mFuncInfo->oid;
}
inline
FunctionHandle&
FunctionHandle::setFunctionCallOptions(uint32_t inFlags) {
mFuncCallOptions = inFlags;
return *this;
}
inline
FunctionHandle&
FunctionHandle::unsetFunctionCallOptions(uint32_t inFlags) {
mFuncCallOptions &= ~inFlags;
return *this;
}
inline
uint32_t
FunctionHandle::getFunctionCallOptions() const {
return mFuncCallOptions;
}
/**
* @brief Wrapper around FunctionCallInvoke
*
* We factor FunctionCallInvoke out in a separate function because local
* variables could potentially be clobbered during the 'longjmp' otherwise.
*/
inline
Datum
FunctionHandle::internalInvoke(FunctionCallInfo inFCInfo) {
Datum result = 0;
MADLIB_PG_TRY {
result = FunctionCallInvoke(inFCInfo);
} MADLIB_PG_CATCH {
throw std::runtime_error(std::string("Exception while invoking '")
+ mSysInfo->functionInformation(mFuncInfo->oid)->getFullName()
+ "'. Error was:\n" + MADLIB_PG_ERROR_DATA()->message);
} MADLIB_PG_END_TRY;
return result;
}
inline
AnyType
FunctionHandle::invoke(AnyType &args) {
madlib_assert(args.isComposite(), std::logic_error(
"FunctionHandle::invoke() called with simple type."));
if (args.numFields() > mFuncInfo->nargs)
throw std::invalid_argument(std::string("More arguments given than "
"expected by '")
+ mSysInfo->functionInformation(mFuncInfo->oid)->getFullName()
+ "'.");
bool hasNulls = false;
for (uint16_t i = 0; i < args.numFields(); ++i)
hasNulls |= args[i].isNull();
// If function is strict, we must not call the function at all
if (mFuncInfo->isstrict && hasNulls)
return AnyType();
if (mFuncInfo->cxx_func &&
!(mFuncCallOptions & GarbageCollectionAfterCall)) {
// We have called this function before, so we can now do a shortcut:
// Directly call the C++ function without any detour through the backend
AnyType::LazyConversionToDatumOverride raii(true);
return mFuncInfo->cxx_func(args);
}
MemoryContext oldContext = NULL;
MemoryContext callContext = NULL;
if (mFuncCallOptions & GarbageCollectionAfterCall) {
// In order to do garbage collection, we need to create a new
// memory context
callContext = AllocSetContextCreate(CurrentMemoryContext,
"C++ AL / FunctionHandle::invoke memory context",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
oldContext = MemoryContextSwitchTo(callContext);
}
#if PG_VERSION_NUM >= 120000
FunctionCallInfoBaseData funcPtrCallInfo;
int nargs = args.numFields();
MemSet(&funcPtrCallInfo, 0, SizeForFunctionCallInfo(nargs));
#else
FunctionCallInfoData funcPtrCallInfo;
#endif
// Initializes all the fields of a FunctionCallInfoData except for the arg[]
// and argnull[] arrays
madlib_InitFunctionCallInfoData(
funcPtrCallInfo,
// FmgrInfo *flinfo -- ptr to lookup info used for this call
mFuncInfo->getFuncMgrInfo(),
// short nargs -- # arguments actually passed
args.numFields(),
// Oid fncollation -- collation for function to use
mSysInfo->collationOID,
// fmNodePtr context -- pass info about context of call
NULL,
// fmNodePtr resultinfo -- pass or return extra info about result
NULL
);
#if PG_VERSION_NUM >= 120000
for (uint16_t i = 0; i < funcPtrCallInfo.nargs; ++i) {
funcPtrCallInfo.args[i].value = args[i].getAsDatum(&funcPtrCallInfo,
mFuncInfo->getArgumentType(i));
funcPtrCallInfo.args[i].isnull = args[i].isNull();
elog(WARNING, "funcPtrCallInfo.args[i].value %d funcPtrCallInfo.args[i].isnull %d", funcPtrCallInfo.args[i].value, funcPtrCallInfo.args[i].isnull);
}
#else
for (uint16_t i = 0; i < funcPtrCallInfo.nargs; ++i) {
funcPtrCallInfo.arg[i]= args[i].getAsDatum(&funcPtrCallInfo,
mFuncInfo->getArgumentType(i));
funcPtrCallInfo.argnull[i] = args[i].isNull();
}
#endif
Datum result = internalInvoke(&funcPtrCallInfo);
if (oldContext) {
MemoryContextSwitchTo(oldContext);
TypeInformation* typeInfo
= mSysInfo->typeInformation(mFuncInfo->rettype);
result = datumCopy(result, typeInfo->isByValue(), typeInfo->getLen());
MemoryContextDelete(callContext);
}
return funcPtrCallInfo.isnull
? AnyType()
: AnyType(mSysInfo, result, mFuncInfo->rettype, /* isMutable */ true);
}
inline
AnyType
FunctionHandle::operator()() {
AnyType nil;
return invoke(nil);
}
// In the following, we define
// AnyType operator()(FunctionHandle::Argument& inArgs1, ...,
// FunctionHandle::Argument& inArgsN)
// using Boost.Preprocessor.
#define MADLIB_APPEND_ARG(z, n, data) \
<< data ## n
#define MADLIB_OPERATOR_DEF(z, n, _ignored) \
inline \
AnyType \
FunctionHandle::operator()( \
BOOST_PP_ENUM_PARAMS_Z(z, BOOST_PP_INC(n), \
FunctionHandle::Argument inArg) \
) { \
AnyType args; \
args BOOST_PP_REPEAT(BOOST_PP_INC(n), MADLIB_APPEND_ARG, inArg); \
return invoke(args); \
}
BOOST_PP_REPEAT(MADLIB_FUNC_MAX_ARGS, MADLIB_OPERATOR_DEF, 0 /* ignored */)
#undef MADLIB_OPERATOR_DEF
#undef MADLIB_APPEND_ARG
inline
SystemInformation*
FunctionHandle::getSysInfo() const {
return mSysInfo;
}
} // namespace postgres
} // namespace dbconnector
} // namespace madlib
#endif // defined(MADLIB_POSTGRES_FUNCTIONHANDLE_IMPL_HPP)