11#include "utils/cstring.hpp"
27 spi_plan(
spi_plan &&p) : kept(p.kept), plan(p.plan), ctx(std::move(p.ctx)) { p.kept =
false; }
29 operator ::SPIPlanPtr() {
30 if (ctx.resets() > 0) {
60 typename T::value_type;
61 typename T::allocator_type;
62} && std::same_as<T, std::vector<typename T::value_type, typename T::allocator_type>>;
78 using iterator_category = std::random_access_iterator_tag;
80 using difference_type = std::ptrdiff_t;
82 ::SPITupleTable *tuptable;
84 mutable std::vector<std::optional<T>> tuples;
89 : tuptable(tuptable), index(0),
90 tuples(std::vector<std::optional<T>>(tuptable->numvals, std::nullopt)) {
91 tuples.reserve(tuptable->numvals);
94 : tuptable(tuptable), index(n),
95 tuples(std::vector<std::optional<T>>(tuptable->numvals, std::nullopt)) {
96 tuples.reserve(tuptable->numvals);
99 bool operator==(
size_t end_index)
const {
return index == end_index; }
100 bool operator!=(
size_t end_index)
const {
return index != end_index; }
102 constexpr T &operator*()
const {
return this->operator[](
static_cast<difference_type
>(index)); }
124 constexpr result_iterator operator+(
const difference_type n)
const noexcept {
133 constexpr result_iterator operator-(difference_type n)
const noexcept {
142 constexpr difference_type operator-(
const result_iterator &other)
const noexcept {
143 return index - other.index;
146 T &operator[](difference_type n)
const {
147 if (tuples.at(n).has_value()) {
148 return tuples.at(n).value();
155 ffi_guard{::SPI_getbinval}(tuptable->vals[n], tuptable->tupdesc, 1, &isnull);
156 ::NullableDatum
datum = {.value =
value, .isnull = isnull};
158 ffi_guard{::SPI_gettypeid}(tuptable->tupdesc, 1),
160 tuples.at(n).emplace(ret);
161 return tuples.at(n).value();
162 }
else if (tuptable->tupdesc->natts == 1) {
165 ffi_guard{::SPI_getbinval}(tuptable->vals[n], tuptable->tupdesc, 1, &isnull);
166 ::NullableDatum
datum = {.value =
value, .isnull = isnull};
168 ffi_guard{::SPI_gettypeid}(tuptable->tupdesc, 1),
170 tuples.at(n).emplace(ret);
171 return tuples.at(n).value();
176 for (
int i = 0; i < tuptable->tupdesc->natts; i++) {
179 ffi_guard{::SPI_getbinval}(tuptable->vals[n], tuptable->tupdesc, i + 1, &isnull);
180 ::NullableDatum
datum = {.value =
value, .isnull = isnull};
182 ret.emplace_back(from_nullable_datum<typename T::value_type>(
183 nd,
ffi_guard{::SPI_gettypeid}(tuptable->tupdesc, i + 1),
186 tuples.at(n).emplace(ret);
188 auto ret = [&]<std::size_t... Is>(std::index_sequence<Is...>) {
192 ffi_guard{::SPI_getbinval}(tuptable->vals[n], tuptable->tupdesc, Is + 1, &isnull);
193 ::NullableDatum
datum = {.value =
value, .isnull = isnull};
195 return from_nullable_datum<utils::tuple_element_t<Is, T>>(
196 nd,
ffi_guard{::SPI_gettypeid}(tuptable->tupdesc, Is + 1),
199 }(std::make_index_sequence<utils::tuple_size_v<T>>{});
200 tuples.at(n).emplace(ret);
202 return tuples.at(n).value();
205 constexpr bool operator==(
const result_iterator &other)
const noexcept {
206 return tuptable == other.tuptable && index == other.index;
208 constexpr bool operator!=(
const result_iterator &other)
const noexcept {
209 return !(tuptable == other.tuptable && index == other.index);
211 constexpr bool operator<(
const result_iterator &other)
const noexcept {
212 return index < other.index;
214 constexpr bool operator>(
const result_iterator &other)
const noexcept {
215 return index > other.index;
217 constexpr bool operator<=(
const result_iterator &other)
const noexcept {
218 return index <= other.index;
220 constexpr bool operator>=(
const result_iterator &other)
const noexcept {
221 return index >= other.index;
224 operator const heap_tuple()
const {
return tuptable->vals[index]; }
230 ::SPITupleTable *table;
232 results(::SPITupleTable *table) : table(table) {
233 auto natts = table->tupdesc->natts;
235 for (
int i = 0; i < natts; i++) {
236 auto oid =
ffi_guard{::SPI_gettypeid}(table->tupdesc, i + 1);
239 throw std::invalid_argument(
240 cppgres::fmt::format(
"invalid return type in position {} ({}), got OID {}", i,
241 utils::type_name<typename Ret::value_type>(),
oid));
245 if (natts != utils::tuple_size_v<Ret>) {
249 throw std::runtime_error(cppgres::fmt::format(
"expected {} return values, got {}",
250 utils::tuple_size_v<Ret>, natts));
253 [&]<std::size_t... Is>(std::index_sequence<Is...>) {
255 auto oid =
ffi_guard{::SPI_gettypeid}(table->tupdesc, Is + 1);
257 if (!
type_traits<utils::tuple_element_t<Is, Ret>>().is(t)) {
258 throw std::invalid_argument(cppgres::fmt::format(
259 "invalid return type in position {} ({}), got OID {}", Is,
260 utils::type_name<utils::tuple_element_t<Is, Ret>>(),
oid));
264 }(std::make_index_sequence<utils::tuple_size_v<Ret>>{});
270 size_t end()
const {
return count(); }
272 size_t count()
const {
return table->numvals; }
278 explicit options() : read_only_(
false), count_(0) {}
279 options(
bool read_only) : read_only_(read_only), count_(0) {}
280 options(
int count) : read_only_(
false), count_(count) {}
281 options(
bool read_only,
int count) : read_only_(read_only), count_(count) {}
283 bool read_only()
const {
return read_only_; }
284 int count()
const {
return count_; }
307 return this->query<Ret>(
query,
options(), std::forward<Args>(args)...);
324 if (executors.top() !=
this) {
325 throw std::runtime_error(
"not a current SPI executor");
327 constexpr size_t nargs =
sizeof...(Args);
329 auto nullable_datums = make_nullable_datums(std::forward<Args>(args)...);
330 auto datums = make_datums(nullable_datums);
331 auto nulls = make_nulls(nullable_datums);
332 auto rc =
ffi_guard{::SPI_execute_with_args}(utils::to_cstring(
query), nargs, types.data(),
333 datums.data(), nulls.data(), opts.read_only(),
335 if (rc == SPI_OK_SELECT || rc == SPI_OK_INSERT_RETURNING || rc == SPI_OK_UPDATE_RETURNING ||
336 rc == SPI_OK_DELETE_RETURNING || (rc == SPI_OK_UTILITY && SPI_tuptable !=
nullptr)
337#if PG_MAJORVERSION_NUM >= 17
338 || rc == SPI_OK_MERGE_RETURNING
344 throw std::runtime_error(
345 fmt::format(
"spi error in `{}`", std::string_view(utils::to_cstring(
query))));
351 if (executors.top() !=
this) {
352 throw std::runtime_error(
"not a current SPI executor");
354 constexpr size_t nargs =
sizeof...(Args);
355 std::array<::Oid, nargs> types = {type_traits<Args>().type_for().oid...};
356 return spi_plan<Args...>(
357 ffi_guard{::SPI_prepare}(utils::to_cstring(
query), nargs, types.data()));
360 template <
typename Ret, convertible_into_nullable_datum... Args>
361 results<Ret>
query(spi_plan<Args...> &
query, Args &&...args) {
362 return this->query<Ret, Args...>(
query, options(), std::forward<Args>(args)...);
365 template <
typename Ret, convertible_into_nullable_datum... Args>
366 results<Ret>
query(spi_plan<Args...> &
query, options &&opts, Args &&...args) {
367 if (executors.top() !=
this) {
368 throw std::runtime_error(
"not a current SPI executor");
370 auto nullable_datums = make_nullable_datums(std::forward<Args>(args)...);
371 auto datums = make_datums(nullable_datums);
372 auto nulls = make_nulls(nullable_datums);
373 auto rc = ffi_guard{::SPI_execute_plan}(
query, datums.data(), nulls.data(), opts.read_only(),
377 return results<Ret>(SPI_tuptable);
379 throw std::runtime_error(
"spi error in a query plan");
383 template <convertible_into_nullable_datum_and_has_a_type... Args>
384 uint64_t execute(std::string_view
query, Args &&...args) {
385 return execute(
query, options(), std::forward<Args>(args)...);
388 template <convertible_into_nullable_datum_and_has_a_type... Args>
389 uint64_t execute(std::string_view
query, options &&opts, Args &&...args) {
390 if (executors.top() !=
this) {
391 throw std::runtime_error(
"not a current SPI executor");
393 constexpr size_t nargs =
sizeof...(Args);
394 std::array<::Oid, nargs> types = {type_traits<Args>(args).type_for().oid...};
395 auto nullable_datums = make_nullable_datums(std::forward<Args>(args)...);
396 auto datums = make_datums(nullable_datums);
397 auto nulls = make_nulls(nullable_datums);
398 auto rc = ffi_guard{::SPI_execute_with_args}(utils::to_cstring(
query), nargs, types.data(),
399 datums.data(), nulls.data(), opts.read_only(),
402 return SPI_processed;
404 throw std::runtime_error(cppgres::fmt::format(
"spi error in `{}`",
query));
409 template <convertible_into_nullable_datum... Args>
410 static auto make_nullable_datums(Args &&...args) {
411 return std::array<nullable_datum,
sizeof...(Args)>{
412 into_nullable_datum(std::forward<Args>(args))...};
415 template <std::
size_t nargs>
416 static std::array<::Datum, nargs> make_datums(
const std::array<nullable_datum, nargs> &values) {
417 std::array<::Datum, nargs> datums{};
418 for (std::size_t i = 0; i < nargs; i++) {
419 datums[i] = values[i].is_null()
421 : static_cast<const ::Datum &>(static_cast<const datum &>(values[i]));
426 template <std::
size_t nargs>
427 static std::array<char, nargs> make_nulls(
428 const std::array<nullable_datum, nargs> &values) {
429 std::array<char, nargs> nulls{};
430 for (std::size_t i = 0; i < nargs; i++) {
431 nulls[i] = values[i].is_null() ?
'n' :
' ';
436 ::MemoryContext before_spi;
440 static inline std::stack<spi_executor *> executors;
441 spi_executor(
int flags) : before_spi(::CurrentMemoryContext) {
442 ffi_guard{::SPI_connect_ext}(flags);
443 spi = ::CurrentMemoryContext;
444 ::CurrentMemoryContext = before_spi;
445 executors.push(
this);
453 auto atomic = cppgres::current_postgres_function::atomic();
454 if (atomic.has_value() && atomic.value()) {
455 throw std::runtime_error(
"must be called in a non-atomic context");
459 void commit(
bool chain =
false) {
460 if (executors.top() !=
this) {
461 throw std::runtime_error(
"not a current SPI executor");
463 ffi_guard(chain ? ::SPI_commit_and_chain : ::SPI_commit)();
466 void rollback(
bool chain =
false) {
467 if (executors.top() !=
this) {
468 throw std::runtime_error(
"not a current SPI executor");
470 ffi_guard(chain ? ::SPI_rollback_and_chain : ::SPI_rollback)();
Definition: executor.hpp:59
Definition: record.hpp:444
Definition: datum.hpp:176
Definition: datum.hpp:205
Definition: executor.hpp:21
Definition: datum.hpp:213
Definition: cstring.hpp:21
Definition: executor.hpp:56
Heap tuple convenience wrapper.
Definition: heap_tuple.hpp:11
Definition: memory.hpp:103
Definition: memory.hpp:290
Definition: executor.hpp:277
Definition: executor.hpp:77
Definition: executor.hpp:229
SPI executor API
Definition: executor.hpp:67
results< Ret > query(utils::convertible_to_cstring auto query, options &&opts, Args &&...args)
Queries using a string view.
Definition: executor.hpp:323
spi_executor()
Creates an SPI executor.
Definition: executor.hpp:71
results< Ret > query(utils::convertible_to_cstring auto query, Args &&...args)
Queries using a string view.
Definition: executor.hpp:306
Definition: executor.hpp:449
spi_executor()
Creates an SPI executor.
Definition: executor.hpp:71
Definition: executor.hpp:24
Definition: memory.hpp:153
Tuple descriptor operator.
Definition: record.hpp:21
Postgres type.
Definition: type.hpp:21