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();
149 if (tuptable->tupdesc->natts == 1) {
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();
164 for (
int i = 0; i < tuptable->tupdesc->natts; i++) {
167 ffi_guard{::SPI_getbinval}(tuptable->vals[n], tuptable->tupdesc, i + 1, &isnull);
168 ::NullableDatum
datum = {.value =
value, .isnull = isnull};
170 ret.emplace_back(from_nullable_datum<typename T::value_type>(
171 nd,
ffi_guard{::SPI_gettypeid}(tuptable->tupdesc, i + 1),
174 tuples.emplace(std::next(tuples.begin(), n), std::in_place, ret);
176 auto ret = [&]<std::size_t... Is>(std::index_sequence<Is...>) {
180 ffi_guard{::SPI_getbinval}(tuptable->vals[n], tuptable->tupdesc, Is + 1, &isnull);
181 ::NullableDatum
datum = {.value =
value, .isnull = isnull};
183 return from_nullable_datum<utils::tuple_element_t<Is, T>>(
184 nd,
ffi_guard{::SPI_gettypeid}(tuptable->tupdesc, Is + 1),
187 }(std::make_index_sequence<utils::tuple_size_v<T>>{});
188 tuples.emplace(std::next(tuples.begin(), n), std::in_place, ret);
190 return tuples.at(n).value();
193 constexpr bool operator==(
const result_iterator &other)
const noexcept {
194 return tuptable == other.tuptable && index == other.index;
196 constexpr bool operator!=(
const result_iterator &other)
const noexcept {
197 return !(tuptable == other.tuptable && index == other.index);
199 constexpr bool operator<(
const result_iterator &other)
const noexcept {
200 return index < other.index;
202 constexpr bool operator>(
const result_iterator &other)
const noexcept {
203 return index > other.index;
205 constexpr bool operator<=(
const result_iterator &other)
const noexcept {
206 return index <= other.index;
208 constexpr bool operator>=(
const result_iterator &other)
const noexcept {
209 return index >= other.index;
212 operator const heap_tuple()
const {
return tuptable->vals[index]; }
218 ::SPITupleTable *table;
220 results(::SPITupleTable *table) : table(table) {
221 auto natts = table->tupdesc->natts;
223 for (
int i = 0; i < natts; i++) {
224 auto oid =
ffi_guard{::SPI_gettypeid}(table->tupdesc, i + 1);
227 throw std::invalid_argument(
228 cppgres::fmt::format(
"invalid return type in position {} ({}), got OID {}", i,
229 utils::type_name<typename Ret::value_type>(),
oid));
233 if (natts != utils::tuple_size_v<Ret>) {
237 throw std::runtime_error(cppgres::fmt::format(
"expected {} return values, got {}",
238 utils::tuple_size_v<Ret>, natts));
241 [&]<std::size_t... Is>(std::index_sequence<Is...>) {
243 auto oid =
ffi_guard{::SPI_gettypeid}(table->tupdesc, Is + 1);
245 if (!
type_traits<utils::tuple_element_t<Is, Ret>>().is(t)) {
246 throw std::invalid_argument(cppgres::fmt::format(
247 "invalid return type in position {} ({}), got OID {}", Is,
248 utils::type_name<utils::tuple_element_t<Is, Ret>>(),
oid));
252 }(std::make_index_sequence<utils::tuple_size_v<Ret>>{});
258 size_t end()
const {
return count(); }
260 size_t count()
const {
return table->numvals; }
266 explicit options() : read_only_(
false), count_(0) {}
267 options(
bool read_only) : read_only_(read_only), count_(0) {}
268 options(
int count) : read_only_(
false), count_(count) {}
269 options(
bool read_only,
int count) : read_only_(read_only), count_(count) {}
271 bool read_only()
const {
return read_only_; }
272 int count()
const {
return count_; }
295 return this->query<Ret>(
query,
options(), std::forward<Args>(args)...);
312 if (executors.top() !=
this) {
313 throw std::runtime_error(
"not a current SPI executor");
315 constexpr size_t nargs =
sizeof...(Args);
317 std::array<::Datum, nargs> datums = {into_nullable_datum(args)...};
318 std::array<const char, nargs> nulls = {into_nullable_datum(args).is_null() ?
'n' :
' ' ...};
319 auto rc =
ffi_guard{::SPI_execute_with_args}(utils::to_cstring(
query), nargs, types.data(),
320 datums.data(), nulls.data(), opts.read_only(),
326 throw std::runtime_error(
"spi error");
332 if (executors.top() !=
this) {
333 throw std::runtime_error(
"not a current SPI executor");
335 constexpr size_t nargs =
sizeof...(Args);
336 std::array<::Oid, nargs> types = {type_traits<Args>().type_for().oid...};
337 return spi_plan<Args...>(
338 ffi_guard{::SPI_prepare}(utils::to_cstring(
query), nargs, types.data()));
341 template <
typename Ret, convertible_into_nullable_datum... Args>
342 results<Ret>
query(spi_plan<Args...> &
query, Args &&...args) {
343 return this->query<Ret, Args...>(
query, options(), std::forward<Args>(args)...);
346 template <
typename Ret, convertible_into_nullable_datum... Args>
347 results<Ret>
query(spi_plan<Args...> &
query, options &&opts, Args &&...args) {
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<::Datum, nargs> datums = {into_nullable_datum(args)...};
353 std::array<const char, nargs> nulls = {into_nullable_datum(args).is_null() ?
'n' :
' ' ...};
354 auto rc = ffi_guard{::SPI_execute_plan}(
query, datums.data(), nulls.data(), opts.read_only(),
358 return results<Ret>(SPI_tuptable);
360 throw std::runtime_error(
"spi error");
364 template <convertible_into_nullable_datum_and_has_a_type... Args>
365 uint64_t execute(std::string_view
query, Args &&...args) {
366 return execute(
query, options(), std::forward<Args>(args)...);
369 template <convertible_into_nullable_datum_and_has_a_type... Args>
370 uint64_t execute(std::string_view
query, options &&opts, Args &&...args) {
371 if (executors.top() !=
this) {
372 throw std::runtime_error(
"not a current SPI executor");
374 constexpr size_t nargs =
sizeof...(Args);
375 std::array<::Oid, nargs> types = {type_traits<Args>(args...).type_for().oid...};
376 std::array<::Datum, nargs> datums = {into_nullable_datum(args)...};
377 std::array<const char, nargs> nulls = {into_nullable_datum(args).is_null() ?
'n' :
' ' ...};
378 auto rc = ffi_guard{::SPI_execute_with_args}(
query.data(), nargs, types.data(), datums.data(),
379 nulls.data(), opts.read_only(), opts.count());
381 return SPI_processed;
383 throw std::runtime_error(cppgres::fmt::format(
"spi error"));
388 ::MemoryContext before_spi;
392 static inline std::stack<spi_executor *> executors;
393 spi_executor(
int flags) : before_spi(::CurrentMemoryContext) {
394 ffi_guard{::SPI_connect_ext}(flags);
395 spi = ::CurrentMemoryContext;
396 ::CurrentMemoryContext = before_spi;
397 executors.push(
this);
405 auto atomic = cppgres::current_postgres_function::atomic();
406 if (atomic.has_value() && atomic.value()) {
407 throw std::runtime_error(
"must be called in a non-atomic context");
411 void commit(
bool chain =
false) {
412 if (executors.top() !=
this) {
413 throw std::runtime_error(
"not a current SPI executor");
415 ffi_guard(chain ? ::SPI_commit_and_chain : ::SPI_commit)();
418 void rollback(
bool chain =
false) {
419 if (executors.top() !=
this) {
420 throw std::runtime_error(
"not a current SPI executor");
422 ffi_guard(chain ? ::SPI_rollback_and_chain : ::SPI_rollback)();
Definition: executor.hpp:59
Definition: datum.hpp:160
Definition: datum.hpp:189
Definition: executor.hpp:21
Definition: datum.hpp:197
Definition: cstring.hpp:21
Definition: executor.hpp:56
Heap tuple convenience wrapper.
Definition: heap_tuple.hpp:11
Definition: memory.hpp:61
Definition: memory.hpp:243
Definition: executor.hpp:265
Definition: executor.hpp:77
Definition: executor.hpp:217
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:311
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:294
Definition: executor.hpp:401
spi_executor()
Creates an SPI executor.
Definition: executor.hpp:71
Definition: executor.hpp:24
Definition: memory.hpp:111
Tuple descriptor operator.
Definition: record.hpp:18
Postgres type.
Definition: type.hpp:20