19template <
typename Tuple, std::size_t... Is>
20constexpr bool all_convertible_from_nullable(std::index_sequence<Is...>) {
21 return ((convertible_from_nullable_datum<
22 utils::remove_optional_t<utils::tuple_element_t<Is, Tuple>>>) &&
28 typename utils::tuple_size<T>::type;
29} && all_convertible_from_nullable<T>(std::make_index_sequence<utils::tuple_size_v<T>>{});
38 spi_plan(
spi_plan &&p) : kept(p.kept), plan(p.plan), ctx(std::move(p.ctx)) { p.kept =
false; }
40 operator ::SPIPlanPtr() {
41 if (ctx.resets() > 0) {
48 ffi_guarded(::SPI_keepplan)(*this);
54 ffi_guarded(::SPI_freeplan)(*this);
78 ffi_guarded(::SPI_finish)();
83 using iterator_category = std::random_access_iterator_tag;
85 using difference_type = std::ptrdiff_t;
87 ::SPITupleTable *tuptable;
89 mutable std::vector<std::optional<T>> tuples;
94 : tuptable(tuptable), index(0),
95 tuples(std::vector<std::optional<T>>(tuptable->numvals, std::nullopt)) {
96 tuples.reserve(tuptable->numvals);
99 : tuptable(tuptable), index(n),
100 tuples(std::vector<std::optional<T>>(tuptable->numvals, std::nullopt)) {
101 tuples.reserve(tuptable->numvals);
104 bool operator==(
size_t end_index)
const {
return index == end_index; }
105 bool operator!=(
size_t end_index)
const {
return index != end_index; }
107 constexpr T &operator*()
const {
return this->operator[](
static_cast<difference_type
>(index)); }
127 constexpr result_iterator operator+(
const difference_type n)
const noexcept {
136 constexpr result_iterator operator-(difference_type n)
const noexcept {
145 constexpr difference_type operator-(
const result_iterator &other)
const noexcept {
146 return index - other.index;
149 T &operator[](difference_type n)
const {
150 if (tuples.at(n).has_value()) {
151 return tuples.at(n).value();
154 if (tuptable->tupdesc->natts == 1) {
158 ffi_guarded(::SPI_getbinval)(tuptable->vals[n], tuptable->tupdesc, 1, &isnull);
159 ::NullableDatum
datum = {.value = value, .isnull = isnull};
162 tuples.emplace(std::next(tuples.begin(), n), std::in_place, ret);
163 return tuples.at(n).value();
166 auto ret = [&]<std::size_t... Is>(std::index_sequence<Is...>) {
170 ffi_guarded(::SPI_getbinval)(tuptable->vals[n], tuptable->tupdesc, Is + 1, &isnull);
171 ::NullableDatum
datum = {.value = value, .isnull = isnull};
173 return from_nullable_datum<utils::tuple_element_t<Is, T>>(
176 }(std::make_index_sequence<utils::tuple_size_v<T>>{});
177 tuples.emplace(std::next(tuples.begin(), n), std::in_place, ret);
178 return tuples.at(n).value();
181 constexpr bool operator==(
const result_iterator &other)
const noexcept {
182 return tuptable == other.tuptable && index == other.index;
184 constexpr bool operator!=(
const result_iterator &other)
const noexcept {
185 return !(tuptable == other.tuptable && index == other.index);
187 constexpr bool operator<(
const result_iterator &other)
const noexcept {
188 return index < other.index;
190 constexpr bool operator>(
const result_iterator &other)
const noexcept {
191 return index > other.index;
193 constexpr bool operator<=(
const result_iterator &other)
const noexcept {
194 return index <= other.index;
196 constexpr bool operator>=(
const result_iterator &other)
const noexcept {
197 return index >= other.index;
204 ::SPITupleTable *table;
206 results(::SPITupleTable *table) : table(table) {
207 auto natts = table->tupdesc->natts;
208 [&]<std::size_t... Is>(std::index_sequence<Is...>) {
210 auto oid = ffi_guarded(::SPI_gettypeid)(table->tupdesc, Is + 1);
211 auto t =
type{.oid = oid};
212 if (!
type_traits<utils::tuple_element_t<Is, Ret>>::is(t)) {
213 throw std::invalid_argument(
214 std::format(
"invalid return type in position {} ({}), got OID {}", Is,
215 utils::type_name<utils::tuple_element_t<Is, Ret>>(), oid));
219 }(std::make_index_sequence<
sizeof...(Args)>{});
220 if (natts != utils::tuple_size_v<Ret>) {
224 throw std::runtime_error(
225 std::format(
"expected {} return values, got {}", utils::tuple_size_v<Ret>, natts));
231 size_t end()
const {
return table->numvals; }
244 if (executors.top() !=
this) {
245 throw std::runtime_error(
"not a current SPI executor");
247 constexpr size_t nargs =
sizeof...(Args);
249 std::array<::Datum, nargs> datums = {into_nullable_datum(args)...};
250 std::array<const char, nargs> nulls = {into_nullable_datum(args).is_null() ?
'n' :
' ' ...};
251 auto rc = ffi_guarded(::SPI_execute_with_args)(
query.data(), nargs, types.data(), datums.data(),
252 nulls.data(),
false, 0);
255 return results<Ret, Args...>(SPI_tuptable);
257 throw std::runtime_error(
"spi error");
263 if (executors.top() !=
this) {
264 throw std::runtime_error(
"not a current SPI executor");
266 constexpr size_t nargs =
sizeof...(Args);
267 std::array<::Oid, nargs> types = {type_traits<Args>::type_for().oid...};
268 return spi_plan<Args...>(ffi_guarded(::SPI_prepare)(
query.data(), nargs, types.data()));
271 template <datumable_tuple Ret, convertible_into_nullable_datum... Args>
272 results<Ret, Args...>
query(spi_plan<Args...> &
query, Args &&...args) {
273 if (executors.top() !=
this) {
274 throw std::runtime_error(
"not a current SPI executor");
276 constexpr size_t nargs =
sizeof...(Args);
277 std::array<::Datum, nargs> datums = {into_nullable_datum(args)...};
278 std::array<const char, nargs> nulls = {into_nullable_datum(args).is_null() ?
'n' :
' ' ...};
279 auto rc = ffi_guarded(::SPI_execute_plan)(
query, datums.data(), nulls.data(),
false, 0);
282 return results<Ret, Args...>(SPI_tuptable);
284 throw std::runtime_error(
"spi error");
288 template <convertible_into_nullable_datum_and_has_a_type... Args>
289 uint64_t execute(std::string_view
query, Args &&...args) {
290 if (executors.top() !=
this) {
291 throw std::runtime_error(
"not a current SPI executor");
293 constexpr size_t nargs =
sizeof...(Args);
294 std::array<::Oid, nargs> types = {type_traits<Args>::type_for().oid...};
295 std::array<::Datum, nargs> datums = {into_nullable_datum(args)...};
296 std::array<const char, nargs> nulls = {into_nullable_datum(args).is_null() ?
'n' :
' ' ...};
297 auto rc = ffi_guarded(::SPI_execute_with_args)(
query.data(), nargs, types.data(), datums.data(),
298 nulls.data(),
false, 0);
300 return SPI_processed;
302 throw std::runtime_error(std::format(
"spi error"));
307 ::MemoryContext before_spi;
311 static inline std::stack<spi_executor *> executors;
312 spi_executor(
int flags) : before_spi(::CurrentMemoryContext) {
313 ffi_guarded(::SPI_connect_ext)(flags);
314 spi = ::CurrentMemoryContext;
315 ::CurrentMemoryContext = before_spi;
316 executors.push(
this);
324 auto atomic = cppgres::current_postgres_function::atomic();
325 if (atomic.has_value() && atomic.value()) {
326 throw std::runtime_error(
"must be called in a non-atomic context");
330 void commit(
bool chain =
false) {
331 if (executors.top() !=
this) {
332 throw std::runtime_error(
"not a current SPI executor");
334 ffi_guarded(chain ? ::SPI_commit_and_chain : ::SPI_commit)();
337 void rollback(
bool chain =
false) {
338 if (executors.top() !=
this) {
339 throw std::runtime_error(
"not a current SPI executor");
341 ffi_guarded(chain ? ::SPI_rollback_and_chain : ::SPI_rollback)();
Definition: datum.hpp:109
Definition: datum.hpp:151
Definition: executor.hpp:32
Definition: datum.hpp:159
Definition: executor.hpp:27
Definition: executor.hpp:67
Definition: memory.hpp:61
Definition: memory.hpp:239
Definition: executor.hpp:82
Definition: executor.hpp:203
SPI executor API
Definition: executor.hpp:72
spi_executor()
Creates an SPI executor.
Definition: executor.hpp:76
results< Ret, Args... > query(std::string_view query, Args &&...args)
Queries using a string view.
Definition: executor.hpp:243
Definition: executor.hpp:320
spi_executor()
Creates an SPI executor.
Definition: executor.hpp:76
Definition: executor.hpp:35
Definition: memory.hpp:107
Postgres type.
Definition: type.hpp:20