11#include "syscache.hpp"
38template <
typename Func>
40 requires {
typename utils::function_traits::function_traits<Func>::argument_types; } &&
44 { std::apply(f, args) } -> convertible_into_nullable_datum_or_set_iterator_or_void;
50 operator FunctionCallInfo()
const {
return info_; }
55 short nargs()
const {
return info_->nargs; }
62 return std::views::iota(0,
nargs()) | std::views::transform([
this](
int i) ->
nullable_datum {
71 return std::views::iota(0,
nargs()) | std::views::transform([
this](
int i) ->
type {
72 return {.oid =
ffi_guard{::get_fn_expr_argtype}(info_->flinfo, i)};
81 return std::views::iota(0,
nargs()) | std::views::transform([
this](
int i) ->
value {
83 {.oid = ffi_guard{::get_fn_expr_argtype}(info_->flinfo, i)});
100 ::FunctionCallInfo info_;
105 static std::optional<bool> atomic() {
106 if (!calls.empty()) {
107 auto ctx = calls.top()->context;
108 if (ctx !=
nullptr && IsA(ctx, CallContext)) {
109 return reinterpret_cast<::CallContext *
>(ctx)->atomic;
116 static std::optional<function_call_info> call_info() {
117 if (!calls.empty()) {
128 ~handle() { calls.pop(); }
136 static handle push(::FunctionCallInfo fcinfo) {
140 static void pop() { calls.pop(); }
142 static inline std::stack<::FunctionCallInfo> calls;
162 using argument_types =
typename traits::argument_types;
163 using return_type = utils::function_traits::invoke_result_from_tuple_t<Func, argument_types>;
164 static constexpr std::size_t arity = traits::arity;
173 auto rettype =
type{.oid =
ffi_guard{::get_fn_expr_rettype}(fc->flinfo)};
174 auto retset = fc->flinfo->fn_retset;
178 auto rsinfo =
reinterpret_cast<::ReturnSetInfo *
>(fc->resultinfo);
179 if (rsinfo ==
nullptr) {
180 report(ERROR,
"caller is not expecting a set");
184 if (!OidIsValid(rettype.oid)) {
187 rettype =
type{.oid = (*cache).prorettype};
188 retset = (*cache).proretset;
193 using set_value_type = set_iterator_traits<return_type>::value_type;
195 report(ERROR,
"unexpected set's return type, can't convert `%s` into `%.*s`",
196 rettype.name().data(), utils::type_name<set_value_type>().length(),
197 utils::type_name<set_value_type>().data());
201 "unexpected return type, set is expected, but `%.*s` does not conform to "
202 "`cppgres::datumable_iterator`",
203 utils::type_name<return_type>().length(), utils::type_name<return_type>().data());
206 report(ERROR,
"unexpected return type, can't convert `%s` into `%.*s`",
207 rettype.name().data(), utils::type_name<return_type>().length(),
208 utils::type_name<return_type>().data());
212 short accounted_for_args = 0;
213 auto t = [&]<std::size_t... Is>(std::index_sequence<Is...>) {
214 return argument_types{([&]() -> utils::tuple_element_t<Is, argument_types> {
215 using ptyp = utils::tuple_element_t<Is, argument_types>;
216 auto typ =
type{.oid =
ffi_guard{::get_fn_expr_argtype}(fc->flinfo, Is)};
217 if (!OidIsValid(typ.oid)) {
220 if ((*cache).proargtypes.dim1 > Is) {
221 typ =
type{.oid = (*cache).proargtypes.values[Is]};
225 report(ERROR,
"unexpected type in position %d, can't convert `%s` into `%.*s`", Is,
226 typ.name().data(), utils::type_name<ptyp>().length(),
227 utils::type_name<ptyp>().data());
229 accounted_for_args++;
230 return from_nullable_datum<ptyp>(
nullable_datum(fc->args[Is]), typ.oid);
232 }(std::make_index_sequence<utils::tuple_size_v<argument_types>>{});
234 if (arity != accounted_for_args) {
235 report(ERROR,
"expected %d arguments, got %d instead", arity, accounted_for_args);
238 auto call_handle = current_postgres_function::push(fc);
241 auto rsinfo =
reinterpret_cast<::ReturnSetInfo *
>(fc->resultinfo);
243 using set_value_type = set_iterator_traits<return_type>::value_type;
244 if constexpr (std::same_as<set_value_type, record>) {
246 auto natts = rsinfo->expectedDesc ==
nullptr ? -1 : rsinfo->expectedDesc->natts;
248 rsinfo->returnMode = SFRM_Materialize;
252 ::Tuplestorestate *tupstore =
ffi_guard{::tuplestore_begin_heap}(
253 (rsinfo->allowedModes & SFRM_Materialize_Random) == SFRM_Materialize_Random,
false,
255 rsinfo->setResult = tupstore;
257 auto res = std::apply(func, t);
259 bool checked =
false;
261 auto nargs = r.attributes();
263 if (rsinfo->expectedDesc !=
nullptr && nargs != natts) {
264 throw std::runtime_error(
265 cppgres::fmt::format(
"expected record with {} value{}, got {} instead", nargs,
266 nargs == 1 ?
"" :
"s", natts));
268 if (rsinfo->expectedDesc !=
nullptr &&
269 !r.get_tuple_descriptor().equal_types(
271 throw std::runtime_error(
"expected and returned records do not match");
276 ffi_guard{::tuplestore_puttuple}(tupstore, r);
281 constexpr auto nargs = utils::tuple_size_v<set_value_type>;
283 auto natts = rsinfo->expectedDesc->natts;
285 if (nargs != natts) {
286 throw std::runtime_error(cppgres::fmt::format(
"expected set with {} value{}, got {} instead",
287 nargs, nargs == 1 ?
"" :
"s", natts));
290 [&]<std::size_t... Is>(std::index_sequence<Is...>) {
292 auto oid =
ffi_guard{::SPI_gettypeid}(rsinfo->expectedDesc, Is + 1);
294 using typ = utils::tuple_element_t<Is, set_value_type>;
296 throw std::invalid_argument(
297 cppgres::fmt::format(
"invalid type in record's position {} ({}), got OID {}", Is,
298 utils::type_name<typ>(),
oid));
302 }(std::make_index_sequence<nargs>{});
304 rsinfo->returnMode = SFRM_Materialize;
308 ::Tuplestorestate *tupstore =
ffi_guard{::tuplestore_begin_heap}(
309 (rsinfo->allowedModes & SFRM_Materialize_Random) == SFRM_Materialize_Random,
false,
311 rsinfo->setResult = tupstore;
313 auto result = std::apply(func, t);
315 for (
auto it : result) {
316 CHECK_FOR_INTERRUPTS();
317 std::array<::Datum, nargs> values = std::apply(
318 [](
auto &&...elems) -> std::array<::Datum,
sizeof...(elems)> {
319 return {into_nullable_datum(elems)...};
322 std::array<bool, nargs> isnull = std::apply(
323 [](
auto &&...elems) -> std::array<
bool,
sizeof...(elems)> {
324 return {into_nullable_datum(elems).is_null()...};
327 ffi_guard{::tuplestore_putvalues}(tupstore, rsinfo->expectedDesc, values.data(),
335 if constexpr (std::same_as<return_type, void>) {
339 auto result = std::apply(func, t);
345 return nd.operator const ::Datum &();
349 __builtin_unreachable();
355 template <std::size_t... I>
356 static auto make_arg_tuple(std::index_sequence<I...>)
357 -> std::tuple<std::tuple_element_t<I, std::tuple<Arg...>>...>;
359 using arg_types =
decltype(make_arg_tuple(std::make_index_sequence<
sizeof...(Arg) - 1>{}));
360 using ret_type = std::tuple_element_t<
sizeof...(Arg) - 1, std::tuple<Arg...>>;
367 ::List *fname = list_make2(::makeString(
const_cast<char *
>(schema)),
368 ::makeString(
const_cast<char *
>(
name)));
369 std::array<::Oid,
sizeof...(Arg)> argtypes = {
type_traits<Arg>().type_for().oid...};
370 return ffi_guard{::LookupFuncName}(fname,
static_cast<int>(
sizeof...(Arg) - 1),
371 argtypes.data(),
false);
378 ::List *fname = list_make1(::makeString(
const_cast<char *
>(
name)));
379 std::array<::Oid,
sizeof...(Arg)> argtypes = {
type_traits<Arg>().type_for().oid...};
380 return ffi_guard{::LookupFuncName}(fname,
static_cast<int>(
sizeof...(Arg) - 1),
381 argtypes.data(),
false);
388 auto &argtypes = (*p).proargtypes;
390 for (
int i = 0; i < argtypes.dim1; i++) {
392 if (types[i] !=
type{UNKNOWNOID} &&
393 arg != types[i].oid) {
394 throw std::runtime_error(cppgres::fmt::format(
"expected type {} for argument {}, got {}",
395 types[i].
name(), i,
type{.oid = arg}.name()));
399 rettype_ = (*p).prorettype;
403 throw std::runtime_error(cppgres::fmt::format(
"expected return type {}, got {}",
405 type{.oid = rettype_}.name()));
407 strict_ = (*p).proisstrict;
411 template <
typename... Args>
static constexpr bool convertible_args() {
412 if constexpr (
sizeof...(Args) !=
sizeof...(Arg) - 1) {
415 return []<std::size_t... I>(std::index_sequence<I...>) {
417 std::convertible_to<std::decay_t<Args>, std::tuple_element_t<I, std::tuple<Arg...>>> &&
419 }(std::make_index_sequence<
sizeof...(Args)>{});
422 ret_type operator()(
auto... args)
requires(self::convertible_args<
decltype(args)...>())
424 bool any_nulls =
false;
425 auto optval = []<std::size_t I>(
auto arg) -> ::Datum {
426 using nth_type = std::tuple_element_t<I, std::tuple<Arg...>>;
427 if constexpr (std::same_as<std::nullopt_t,
decltype(arg)>) {
436 auto isnull = [&any_nulls](
auto arg) {
437 if constexpr (std::same_as<std::nullopt_t,
decltype(arg)>) {
443 any_nulls = !arg.has_value();
444 return !arg.has_value();
448 return [&]<std::size_t... I>(std::index_sequence<I...>) -> ret_type {
449 LOCAL_FCINFO(fcinfo,
sizeof...(args));
454 InitFunctionCallInfoData(*fcinfo, &flinfo,
sizeof...(args), InvalidOid, NULL, NULL);
456 ((fcinfo->args[I].value = optval.template operator()<I>(args)), ...);
457 ((fcinfo->args[I].isnull = isnull(args)), ...);
459 if (any_nulls && strict_) {
468 if (fcinfo->isnull) {
473 }(std::make_index_sequence<
sizeof...(Arg) - 1>{});
476 const oid &function_oid()
const {
return oid_; }
486 static function<Args...> from_datum(
const datum &d,
oid, std::optional<memory_context> ctx) {
496 static bool is(
const type &t) {
497 return t.oid == REGPROCEDUREOID || t.oid == OIDOID || t.oid == REGPROCOID;
499 static constexpr type type_for() {
return type{.oid = REGPROCEDUREOID}; }
505 ffi_guard{::getTypeOutputInfo}(type_traits<T>().type_for().oid, &foutoid, &typisvarlena);
506 return function<T, const char *>(foutoid);
509template <has_type_traits T> function<T, const char *> output_function(T &&v) {
512 ffi_guard{::getTypeOutputInfo}(type_traits<T>(v).type_for().oid, &foutoid, &typisvarlena);
513 return function<T, const char *>(foutoid);
516template <has_type_traits T> function<T, const char *> output_function(T &v) {
519 ffi_guard{::getTypeOutputInfo}(type_traits<T>(v).type_for().oid, &foutoid, &typisvarlena);
520 return function<T, const char *>(foutoid);
523static function<cppgres::value, const char *> output_function(
const type &t) {
526 ffi_guard{::getTypeOutputInfo}(t.oid, &foutoid, &typisvarlena);
527 return function<cppgres::value, const char *>(foutoid);
Definition: function.hpp:27
Definition: datum.hpp:213
Function that operates on values of Postgres types.
Definition: function.hpp:39
#define postgres_function(name, function)
Export a C++ function as a Postgres function.
Definition: cppgres.hpp:100
Definition: datum.hpp:217
Definition: memory.hpp:118
Definition: collation.hpp:8
Definition: function.hpp:103
A trait to convert from and into a cppgres::datum.
Definition: datum.hpp:114
static T from_nullable_datum(const nullable_datum &d, const oid oid, std::optional< memory_context > context=std::nullopt)=delete
Convert from a nullable datum.
static datum into_datum(const T &d)=delete
Convert datum into a type.
Definition: datum.hpp:146
Wraps a C++ function to catch exceptions and report them as Postgres errors.
Definition: guard.hpp:64
Definition: function.hpp:47
auto arg_types() const
argument types
Definition: function.hpp:70
auto args() const
passed arguments
Definition: function.hpp:61
auto arg_values() const
typed passed argument
Definition: function.hpp:80
type return_type() const
return type
Definition: function.hpp:95
short nargs() const
number of arguments actually passed
Definition: function.hpp:55
oid called_function_oid() const
called function OID
Definition: function.hpp:90
Definition: function.hpp:353
Definition: memory.hpp:217
Definition: memory.hpp:85
Postgres function implemented in C++.
Definition: function.hpp:155
auto operator()(FunctionCallInfo fc) -> ::Datum
Definition: function.hpp:169
Definition: syscache.hpp:27
Tuple descriptor operator.
Definition: record.hpp:18
Postgres type.
Definition: type.hpp:20
Definition: function_traits.hpp:37