37template <
typename Func>
39 requires {
typename utils::function_traits::function_traits<Func>::argument_types; } &&
47 } -> convertible_into_nullable_datum_or_set_iterator_or_void;
53 operator FunctionCallInfo()
const {
return info_; }
58 short nargs()
const {
return info_->nargs; }
65 return std::views::iota(0,
nargs()) | std::views::transform([
this](
int i) ->
nullable_datum {
74 return std::views::iota(0,
nargs()) | std::views::transform([
this](
int i) ->
type {
75 return {.oid =
ffi_guard{::get_fn_expr_argtype}(info_->flinfo, i)};
84 return std::views::iota(0,
nargs()) | std::views::transform([
this](
int i) ->
value {
86 {.oid = ffi_guard{::get_fn_expr_argtype}(info_->flinfo, i)});
101 ::FunctionCallInfo info_;
106 static std::optional<bool> atomic() {
107 if (!calls.empty()) {
108 auto ctx = calls.top()->context;
109 if (ctx !=
nullptr && IsA(ctx, CallContext)) {
110 return reinterpret_cast<::CallContext *
>(ctx)->atomic;
117 static std::optional<function_call_info> call_info() {
118 if (!calls.empty()) {
129 ~handle() { calls.pop(); }
137 static handle push(::FunctionCallInfo fcinfo) {
141 static void pop() { calls.pop(); }
143 static inline std::stack<::FunctionCallInfo> calls;
163 using argument_types =
typename traits::argument_types;
164 using return_type = utils::function_traits::invoke_result_from_tuple_t<Func, argument_types>;
165 static constexpr std::size_t arity = traits::arity;
174 auto rettype =
type{.oid =
ffi_guard{::get_fn_expr_rettype}(fc->flinfo)};
175 auto retset = fc->flinfo->fn_retset;
179 auto rsinfo =
reinterpret_cast<::ReturnSetInfo *
>(fc->resultinfo);
180 if (rsinfo ==
nullptr) {
181 report(ERROR,
"caller is not expecting a set");
185 if (!OidIsValid(rettype.oid)) {
188 rettype =
type{.oid = (*cache).prorettype};
189 retset = (*cache).proretset;
194 using set_value_type = set_iterator_traits<return_type>::value_type;
196 report(ERROR,
"unexpected set's return type, can't convert `%s` into `%.*s`",
197 rettype.name().data(), utils::type_name<set_value_type>().length(),
198 utils::type_name<set_value_type>().data());
202 "unexpected return type, set is expected, but `%.*s` does not conform to "
203 "`cppgres::datumable_iterator`",
204 utils::type_name<return_type>().length(), utils::type_name<return_type>().data());
207 report(ERROR,
"unexpected return type, can't convert `%s` into `%.*s`",
208 rettype.name().data(), utils::type_name<return_type>().length(),
209 utils::type_name<return_type>().data());
213 short accounted_for_args = 0;
214 auto t = [&]<std::size_t... Is>(std::index_sequence<Is...>) {
215 return argument_types{([&]() -> utils::tuple_element_t<Is, argument_types> {
216 using ptyp = utils::tuple_element_t<Is, argument_types>;
217 auto typ =
type{.oid =
ffi_guard{::get_fn_expr_argtype}(fc->flinfo, Is)};
218 if (!OidIsValid(typ.oid)) {
221 if ((*cache).proargtypes.dim1 > Is) {
222 typ =
type{.oid = (*cache).proargtypes.values[Is]};
226 report(ERROR,
"unexpected type in position %d, can't convert `%s` into `%.*s`", Is,
227 typ.name().data(), utils::type_name<ptyp>().length(),
228 utils::type_name<ptyp>().data());
230 accounted_for_args++;
231 return from_nullable_datum<ptyp>(
nullable_datum(fc->args[Is]), typ.oid);
233 }(std::make_index_sequence<utils::tuple_size_v<argument_types>>{});
235 if (arity != accounted_for_args) {
236 report(ERROR,
"expected %d arguments, got %d instead", arity, accounted_for_args);
239 auto call_handle = current_postgres_function::push(fc);
242 auto rsinfo =
reinterpret_cast<::ReturnSetInfo *
>(fc->resultinfo);
244 using set_value_type = set_iterator_traits<return_type>::value_type;
245 if constexpr (std::same_as<set_value_type, record>) {
247 auto natts = rsinfo->expectedDesc ==
nullptr ? -1 : rsinfo->expectedDesc->natts;
249 rsinfo->returnMode = SFRM_Materialize;
253 ::Tuplestorestate *tupstore =
ffi_guard{::tuplestore_begin_heap}(
254 (rsinfo->allowedModes & SFRM_Materialize_Random) == SFRM_Materialize_Random,
false,
256 rsinfo->setResult = tupstore;
258 auto res = std::apply(func, t);
260 bool checked =
false;
262 auto nargs = r.attributes();
264 if (rsinfo->expectedDesc !=
nullptr && nargs != natts) {
265 throw std::runtime_error(
266 cppgres::fmt::format(
"expected record with {} value{}, got {} instead", nargs,
267 nargs == 1 ?
"" :
"s", natts));
269 if (rsinfo->expectedDesc !=
nullptr &&
270 !r.get_tuple_descriptor().equal_types(
272 throw std::runtime_error(
"expected and returned records do not match");
277 ffi_guard{::tuplestore_puttuple}(tupstore, r);
282 constexpr auto nargs = utils::tuple_size_v<set_value_type>;
284 auto natts = rsinfo->expectedDesc->natts;
286 if (nargs != natts) {
287 throw std::runtime_error(cppgres::fmt::format(
"expected set with {} value{}, got {} instead",
288 nargs, nargs == 1 ?
"" :
"s", natts));
291 [&]<std::size_t... Is>(std::index_sequence<Is...>) {
293 auto oid =
ffi_guard{::SPI_gettypeid}(rsinfo->expectedDesc, Is + 1);
295 using typ = utils::tuple_element_t<Is, set_value_type>;
297 throw std::invalid_argument(
298 cppgres::fmt::format(
"invalid type in record's position {} ({}), got OID {}", Is,
299 utils::type_name<typ>(),
oid));
303 }(std::make_index_sequence<nargs>{});
305 rsinfo->returnMode = SFRM_Materialize;
309 ::Tuplestorestate *tupstore =
ffi_guard{::tuplestore_begin_heap}(
310 (rsinfo->allowedModes & SFRM_Materialize_Random) == SFRM_Materialize_Random,
false,
312 rsinfo->setResult = tupstore;
314 auto result = std::apply(func, t);
316 for (
auto it : result) {
317 CHECK_FOR_INTERRUPTS();
318 std::array<::Datum, nargs> values = std::apply(
319 [](
auto &&...elems) -> std::array<::Datum,
sizeof...(elems)> {
320 return {into_nullable_datum(elems)...};
323 std::array<bool, nargs> isnull = std::apply(
324 [](
auto &&...elems) -> std::array<
bool,
sizeof...(elems)> {
325 return {into_nullable_datum(elems).is_null()...};
328 ffi_guard{::tuplestore_putvalues}(tupstore, rsinfo->expectedDesc, values.data(),
336 if constexpr (std::same_as<return_type, void>) {
340 auto result = std::apply(func, t);
346 return nd.operator const ::Datum &();
350 __builtin_unreachable();
Definition: function.hpp:26
Definition: datum.hpp:197
Function that operates on values of Postgres types.
Definition: function.hpp:38
#define postgres_function(name, function)
Export a C++ function as a Postgres function.
Definition: cppgres.hpp:98
Definition: datum.hpp:201
Definition: function.hpp:104
Wraps a C++ function to catch exceptions and report them as Postgres errors.
Definition: guard.hpp:64
Definition: function.hpp:50
auto arg_types() const
argument types
Definition: function.hpp:73
auto args() const
passed arguments
Definition: function.hpp:64
auto arg_values() const
typed passed argument
Definition: function.hpp:83
type return_type() const
return type
Definition: function.hpp:98
short nargs() const
number of arguments actually passed
Definition: function.hpp:58
oid called_function_oid() const
called function OID
Definition: function.hpp:93
Definition: memory.hpp:193
Definition: memory.hpp:61
Postgres function implemented in C++.
Definition: function.hpp:156
auto operator()(FunctionCallInfo fc) -> ::Datum
Definition: function.hpp:170
Definition: syscache.hpp:27
Tuple descriptor operator.
Definition: record.hpp:18
Postgres type.
Definition: type.hpp:20
Definition: function_traits.hpp:37