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)); }
122 constexpr result_iterator operator+(
const difference_type n)
const noexcept {
131 constexpr result_iterator operator-(difference_type n)
const noexcept {
140 constexpr difference_type operator-(
const result_iterator &other)
const noexcept {
141 return index - other.index;
144 T &operator[](difference_type n)
const {
145 if (tuples.at(n).has_value()) {
146 return tuples.at(n).value();
153 ffi_guard{::SPI_getbinval}(tuptable->vals[n], tuptable->tupdesc, 1, &isnull);
154 ::NullableDatum
datum = {.value =
value, .isnull = isnull};
156 ffi_guard{::SPI_gettypeid}(tuptable->tupdesc, 1),
158 tuples.emplace(std::next(tuples.begin(), n), std::in_place, ret);
159 return tuples.at(n).value();
160 }
else if (tuptable->tupdesc->natts == 1) {
163 ffi_guard{::SPI_getbinval}(tuptable->vals[n], tuptable->tupdesc, 1, &isnull);
164 ::NullableDatum
datum = {.value =
value, .isnull = isnull};
166 ffi_guard{::SPI_gettypeid}(tuptable->tupdesc, 1),
168 tuples.emplace(std::next(tuples.begin(), n), std::in_place, ret);
169 return tuples.at(n).value();
174 for (
int i = 0; i < tuptable->tupdesc->natts; i++) {
177 ffi_guard{::SPI_getbinval}(tuptable->vals[n], tuptable->tupdesc, i + 1, &isnull);
178 ::NullableDatum
datum = {.value =
value, .isnull = isnull};
180 ret.emplace_back(from_nullable_datum<typename T::value_type>(
181 nd,
ffi_guard{::SPI_gettypeid}(tuptable->tupdesc, i + 1),
184 tuples.emplace(std::next(tuples.begin(), n), std::in_place, ret);
186 auto ret = [&]<std::size_t... Is>(std::index_sequence<Is...>) {
190 ffi_guard{::SPI_getbinval}(tuptable->vals[n], tuptable->tupdesc, Is + 1, &isnull);
191 ::NullableDatum
datum = {.value =
value, .isnull = isnull};
193 return from_nullable_datum<utils::tuple_element_t<Is, T>>(
194 nd,
ffi_guard{::SPI_gettypeid}(tuptable->tupdesc, Is + 1),
197 }(std::make_index_sequence<utils::tuple_size_v<T>>{});
198 tuples.emplace(std::next(tuples.begin(), n), std::in_place, ret);
200 return tuples.at(n).value();
203 constexpr bool operator==(
const result_iterator &other)
const noexcept {
204 return tuptable == other.tuptable && index == other.index;
206 constexpr bool operator!=(
const result_iterator &other)
const noexcept {
207 return !(tuptable == other.tuptable && index == other.index);
209 constexpr bool operator<(
const result_iterator &other)
const noexcept {
210 return index < other.index;
212 constexpr bool operator>(
const result_iterator &other)
const noexcept {
213 return index > other.index;
215 constexpr bool operator<=(
const result_iterator &other)
const noexcept {
216 return index <= other.index;
218 constexpr bool operator>=(
const result_iterator &other)
const noexcept {
219 return index >= other.index;
222 operator const heap_tuple()
const {
return tuptable->vals[index]; }
228 ::SPITupleTable *table;
230 results(::SPITupleTable *table) : table(table) {
231 auto natts = table->tupdesc->natts;
233 for (
int i = 0; i < natts; i++) {
234 auto oid =
ffi_guard{::SPI_gettypeid}(table->tupdesc, i + 1);
237 throw std::invalid_argument(
238 cppgres::fmt::format(
"invalid return type in position {} ({}), got OID {}", i,
239 utils::type_name<typename Ret::value_type>(),
oid));
243 if (natts != utils::tuple_size_v<Ret>) {
247 throw std::runtime_error(cppgres::fmt::format(
"expected {} return values, got {}",
248 utils::tuple_size_v<Ret>, natts));
251 [&]<std::size_t... Is>(std::index_sequence<Is...>) {
253 auto oid =
ffi_guard{::SPI_gettypeid}(table->tupdesc, Is + 1);
255 if (!
type_traits<utils::tuple_element_t<Is, Ret>>().is(t)) {
256 throw std::invalid_argument(cppgres::fmt::format(
257 "invalid return type in position {} ({}), got OID {}", Is,
258 utils::type_name<utils::tuple_element_t<Is, Ret>>(),
oid));
262 }(std::make_index_sequence<utils::tuple_size_v<Ret>>{});
268 size_t end()
const {
return count(); }
270 size_t count()
const {
return table->numvals; }
276 explicit options() : read_only_(
false), count_(0) {}
277 options(
bool read_only) : read_only_(read_only), count_(0) {}
278 options(
int count) : read_only_(
false), count_(count) {}
279 options(
bool read_only,
int count) : read_only_(read_only), count_(count) {}
281 bool read_only()
const {
return read_only_; }
282 int count()
const {
return count_; }
305 return this->query<Ret>(
query,
options(), std::forward<Args>(args)...);
322 if (executors.top() !=
this) {
323 throw std::runtime_error(
"not a current SPI executor");
325 constexpr size_t nargs =
sizeof...(Args);
327 std::array<::Datum, nargs> datums = {into_nullable_datum(args)...};
328 std::array<const char, nargs> nulls = {into_nullable_datum(args).is_null() ?
'n' :
' ' ...};
329 auto rc =
ffi_guard{::SPI_execute_with_args}(utils::to_cstring(
query), nargs, types.data(),
330 datums.data(), nulls.data(), opts.read_only(),
332 if (rc == SPI_OK_SELECT || rc == SPI_OK_INSERT_RETURNING || rc == SPI_OK_UPDATE_RETURNING ||
333 rc == SPI_OK_DELETE_RETURNING || (rc == SPI_OK_UTILITY && SPI_tuptable !=
nullptr)
334#if PG_MAJORVERSION_NUM >= 17
335 || rc == SPI_OK_MERGE_RETURNING
341 throw std::runtime_error(
342 fmt::format(
"spi error in `{}`", std::string_view(utils::to_cstring(
query))));
348 if (executors.top() !=
this) {
349 throw std::runtime_error(
"not a current SPI executor");
351 constexpr size_t nargs =
sizeof...(Args);
352 std::array<::Oid, nargs> types = {type_traits<Args>().type_for().oid...};
353 return spi_plan<Args...>(
354 ffi_guard{::SPI_prepare}(utils::to_cstring(
query), nargs, types.data()));
357 template <
typename Ret, convertible_into_nullable_datum... Args>
358 results<Ret>
query(spi_plan<Args...> &
query, Args &&...args) {
359 return this->query<Ret, Args...>(
query, options(), std::forward<Args>(args)...);
362 template <
typename Ret, convertible_into_nullable_datum... Args>
363 results<Ret>
query(spi_plan<Args...> &
query, options &&opts, Args &&...args) {
364 if (executors.top() !=
this) {
365 throw std::runtime_error(
"not a current SPI executor");
367 constexpr size_t nargs =
sizeof...(Args);
368 std::array<::Datum, nargs> datums = {into_nullable_datum(args)...};
369 std::array<const char, nargs> nulls = {into_nullable_datum(args).is_null() ?
'n' :
' ' ...};
370 auto rc = ffi_guard{::SPI_execute_plan}(
query, datums.data(), nulls.data(), opts.read_only(),
374 return results<Ret>(SPI_tuptable);
376 throw std::runtime_error(
"spi error in a query plan");
380 template <convertible_into_nullable_datum_and_has_a_type... Args>
381 uint64_t execute(std::string_view
query, Args &&...args) {
382 return execute(
query, options(), std::forward<Args>(args)...);
385 template <convertible_into_nullable_datum_and_has_a_type... Args>
386 uint64_t execute(std::string_view
query, options &&opts, Args &&...args) {
387 if (executors.top() !=
this) {
388 throw std::runtime_error(
"not a current SPI executor");
390 constexpr size_t nargs =
sizeof...(Args);
391 std::array<::Oid, nargs> types = {type_traits<Args>(args...).type_for().oid...};
392 std::array<::Datum, nargs> datums = {into_nullable_datum(args)...};
393 std::array<const char, nargs> nulls = {into_nullable_datum(args).is_null() ?
'n' :
' ' ...};
394 auto rc = ffi_guard{::SPI_execute_with_args}(
query.data(), nargs, types.data(), datums.data(),
395 nulls.data(), opts.read_only(), opts.count());
397 return SPI_processed;
399 throw std::runtime_error(cppgres::fmt::format(
"spi error in `{}`",
query));
404 ::MemoryContext before_spi;
408 static inline std::stack<spi_executor *> executors;
409 spi_executor(
int flags) : before_spi(::CurrentMemoryContext) {
410 ffi_guard{::SPI_connect_ext}(flags);
411 spi = ::CurrentMemoryContext;
412 ::CurrentMemoryContext = before_spi;
413 executors.push(
this);
421 auto atomic = cppgres::current_postgres_function::atomic();
422 if (atomic.has_value() && atomic.value()) {
423 throw std::runtime_error(
"must be called in a non-atomic context");
427 void commit(
bool chain =
false) {
428 if (executors.top() !=
this) {
429 throw std::runtime_error(
"not a current SPI executor");
431 ffi_guard(chain ? ::SPI_commit_and_chain : ::SPI_commit)();
434 void rollback(
bool chain =
false) {
435 if (executors.top() !=
this) {
436 throw std::runtime_error(
"not a current SPI executor");
438 ffi_guard(chain ? ::SPI_rollback_and_chain : ::SPI_rollback)();
Definition: executor.hpp:59
Definition: record.hpp:440
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:85
Definition: memory.hpp:267
Definition: executor.hpp:275
Definition: executor.hpp:77
Definition: executor.hpp:227
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:321
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:304
Definition: executor.hpp:417
spi_executor()
Creates an SPI executor.
Definition: executor.hpp:71
Definition: executor.hpp:24
Definition: memory.hpp:135
Tuple descriptor operator.
Definition: record.hpp:18
Postgres type.
Definition: type.hpp:20