Cppgres
Build Postgres extensions in C++
Loading...
Searching...
No Matches
function.hpp
Go to the documentation of this file.
1
4#pragma once
5
6#include "datum.hpp"
7#include "guard.hpp"
8#include "imports.h"
9#include "set.hpp"
10#include "types.hpp"
12#include "utils/utils.hpp"
13
14#include <array>
15#include <complex>
16#include <iostream>
17#include <stack>
18#include <tuple>
19#include <typeinfo>
20
21namespace cppgres {
22
23template <typename T>
26
35template <typename Func>
37 requires { typename utils::function_traits::function_traits<Func>::argument_types; } &&
40 requires(Func f) {
41 {
42 std::apply(
43 f,
45 } -> convertible_into_nullable_datum_or_set_iterator_or_void;
46 };
47
49
50 static std::optional<bool> atomic() {
51 if (!calls.empty()) {
52 auto ctx = calls.top()->context;
53 if (ctx != nullptr && IsA(ctx, CallContext)) {
54 return reinterpret_cast<::CallContext *>(ctx)->atomic;
55 }
56 }
57
58 return std::nullopt;
59 }
60
61 template <datumable_function Func> friend struct postgres_function;
62
63private:
64 struct handle {
65 ~handle() { calls.pop(); }
66
67 friend struct current_postgres_function;
68
69 private:
70 handle() {}
71 };
72
73 static handle push(::FunctionCallInfo fcinfo) {
74 calls.push(fcinfo);
75 return handle{};
76 }
77 static void pop() { calls.pop(); }
78
79 static inline std::stack<::FunctionCallInfo> calls;
80};
81
92template <datumable_function Func> struct postgres_function {
93 Func func;
94
95 explicit postgres_function(Func f) : func(f) {}
96
97 // Use the function_traits to extract argument types.
99 using argument_types = typename traits::argument_types;
100 using return_type = utils::function_traits::invoke_result_from_tuple_t<Func, argument_types>;
101 static constexpr std::size_t arity = traits::arity;
102
106 auto operator()(FunctionCallInfo fc) -> ::Datum {
107
108 argument_types t;
109 if (arity != fc->nargs) {
110 report(ERROR, "expected %d arguments, got %d instead", arity, fc->nargs);
111 } else {
112
113 try {
114 [&]<std::size_t... Is>(std::index_sequence<Is...>) {
115 (([&] {
116 auto ptyp =
117 utils::remove_optional_t<std::remove_reference_t<decltype(std::get<Is>(t))>>();
118 auto typ = type{.oid = ffi_guarded(::get_fn_expr_argtype)(fc->flinfo, Is)};
119 if (!OidIsValid(typ.oid)) {
120 // TODO: not very efficient to look it up every time
121 syscache<Form_pg_proc, Oid> cache(fc->flinfo->fn_oid);
122 if ((*cache).proargtypes.dim1 > Is) {
123 typ = type{.oid = (*cache).proargtypes.values[Is]};
124 } else {
125 return; // skip undefined arguments (happens with type `_in` functions)
126 }
127 }
128 if (!type_traits<decltype(ptyp)>::is(typ)) {
129 report(ERROR, "unexpected type in position %d, can't convert `%s` into `%.*s`", Is,
130 typ.name().data(), utils::type_name<decltype(ptyp)>().length(),
131 utils::type_name<decltype(ptyp)>().data());
132 }
133 std::get<Is>(t) = from_nullable_datum<decltype(ptyp)>(nullable_datum(fc->args[Is]));
134 }()),
135 ...);
136 }(std::make_index_sequence<utils::tuple_size_v<decltype(t)>>{});
137
138 auto call_handle = current_postgres_function::push(fc);
139
140 if constexpr (datumable_iterator<return_type>) {
141 // TODO: For now, let's assume materialized model
142 auto rsinfo = reinterpret_cast<::ReturnSetInfo *>(fc->resultinfo);
143 if (rsinfo == nullptr) {
144 throw std::runtime_error("caller is not expecting a set");
145 }
146 using set_value_type = set_iterator_traits<return_type>::value_type;
147 constexpr auto nargs = utils::tuple_size_v<set_value_type>;
148
149 auto natts = rsinfo->expectedDesc->natts;
150 if (nargs != natts) {
151 throw std::runtime_error(
152 std::format("expected set with {} values, got {} instead", nargs, natts));
153 }
154
155 [&]<std::size_t... Is>(std::index_sequence<Is...>) {
156 (([&] {
157 auto oid = ffi_guarded(::SPI_gettypeid)(rsinfo->expectedDesc, Is + 1);
158 auto t = type{.oid = oid};
159 using typ = utils::tuple_element_t<Is, set_value_type>;
160 if (!type_traits<typ>::is(t)) {
161 throw std::invalid_argument(
162 std::format("invalid type in record's position {} ({}), got OID {}", Is,
163 utils::type_name<typ>(), oid));
164 }
165 }()),
166 ...);
167 }(std::make_index_sequence<nargs>{});
168
169 rsinfo->returnMode = SFRM_Materialize;
170
171 memory_context_scope scope(memory_context(rsinfo->econtext->ecxt_per_query_memory));
172
173 ::Tuplestorestate *tupstore = ffi_guarded(::tuplestore_begin_heap)(
174 (rsinfo->allowedModes & SFRM_Materialize_Random) == SFRM_Materialize_Random, false,
175 work_mem);
176 rsinfo->setResult = tupstore;
177
178 auto result = std::apply(func, t);
179
180 for (auto it : result) {
181 CHECK_FOR_INTERRUPTS();
182 std::array<::Datum, nargs> values = std::apply(
183 [](auto &&...elems) -> std::array<::Datum, sizeof...(elems)> {
184 return {into_nullable_datum(elems)...};
185 },
186 utils::tie(it));
187 std::array<bool, nargs> isnull = std::apply(
188 [](auto &&...elems) -> std::array<bool, sizeof...(elems)> {
189 return {into_nullable_datum(elems).is_null()...};
190 },
191 utils::tie(it));
192 ffi_guarded(::tuplestore_putvalues)(tupstore, rsinfo->expectedDesc, values.data(),
193 isnull.data());
194 }
195
196 fc->isnull = true;
197 return 0;
198 } else {
199 if constexpr (std::same_as<return_type, void>) {
200 std::apply(func, t);
201 return 0;
202 } else {
203 auto result = std::apply(func, t);
204 nullable_datum nd = into_nullable_datum(result);
205 if (nd.is_null()) {
206 fc->isnull = true;
207 return 0;
208 }
209 return nd;
210 }
211 }
212 } catch (const pg_exception &e) {
213 error(e);
214 } catch (const std::exception &e) {
215 report(ERROR, "exception: %s", e.what());
216 } catch (...) {
217 report(ERROR, "some exception occurred");
218 }
219 }
220 __builtin_unreachable();
221 }
222};
223
224} // namespace cppgres
Definition: exception.hpp:7
Function that operates on values of Postgres types.
Definition: function.hpp:36
Definition: set.hpp:15
Definition: datum.hpp:163
Definition: function.hpp:48
Definition: memory.hpp:189
Definition: memory.hpp:61
Definition: datum.hpp:38
Postgres function implemented in C++.
Definition: function.hpp:92
auto operator()(FunctionCallInfo fc) -> ::Datum
Definition: function.hpp:106
Definition: syscache.hpp:26
Definition: type.hpp:41
Postgres type.
Definition: type.hpp:20
Definition: function_traits.hpp:37