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;
216 ::SPITupleTable *table;
218 results(::SPITupleTable *table) : table(table) {
219 auto natts = table->tupdesc->natts;
221 for (
int i = 0; i < natts; i++) {
222 auto oid =
ffi_guard{::SPI_gettypeid}(table->tupdesc, i + 1);
225 throw std::invalid_argument(
226 cppgres::fmt::format(
"invalid return type in position {} ({}), got OID {}", i,
227 utils::type_name<typename Ret::value_type>(),
oid));
231 if (natts != utils::tuple_size_v<Ret>) {
235 throw std::runtime_error(cppgres::fmt::format(
"expected {} return values, got {}",
236 utils::tuple_size_v<Ret>, natts));
239 [&]<std::size_t... Is>(std::index_sequence<Is...>) {
241 auto oid =
ffi_guard{::SPI_gettypeid}(table->tupdesc, Is + 1);
243 if (!
type_traits<utils::tuple_element_t<Is, Ret>>().is(t)) {
244 throw std::invalid_argument(cppgres::fmt::format(
245 "invalid return type in position {} ({}), got OID {}", Is,
246 utils::type_name<utils::tuple_element_t<Is, Ret>>(),
oid));
250 }(std::make_index_sequence<utils::tuple_size_v<Ret>>{});
256 size_t end()
const {
return count(); }
258 size_t count()
const {
return table->numvals; }
264 explicit options() : read_only_(
false), count_(0) {}
265 options(
bool read_only) : read_only_(read_only), count_(0) {}
266 options(
int count) : read_only_(
false), count_(count) {}
267 options(
bool read_only,
int count) : read_only_(read_only), count_(count) {}
269 bool read_only()
const {
return read_only_; }
270 int count()
const {
return count_; }
293 return this->query<Ret>(
query,
options(), std::forward<Args>(args)...);
310 if (executors.top() !=
this) {
311 throw std::runtime_error(
"not a current SPI executor");
313 constexpr size_t nargs =
sizeof...(Args);
315 std::array<::Datum, nargs> datums = {into_nullable_datum(args)...};
316 std::array<const char, nargs> nulls = {into_nullable_datum(args).is_null() ?
'n' :
' ' ...};
317 auto rc =
ffi_guard{::SPI_execute_with_args}(utils::to_cstring(
query), nargs, types.data(),
318 datums.data(), nulls.data(), opts.read_only(),
324 throw std::runtime_error(
"spi error");
330 if (executors.top() !=
this) {
331 throw std::runtime_error(
"not a current SPI executor");
333 constexpr size_t nargs =
sizeof...(Args);
334 std::array<::Oid, nargs> types = {type_traits<Args>().type_for().oid...};
335 return spi_plan<Args...>(
336 ffi_guard{::SPI_prepare}(utils::to_cstring(
query), nargs, types.data()));
339 template <
typename Ret, convertible_into_nullable_datum... Args>
340 results<Ret>
query(spi_plan<Args...> &
query, Args &&...args) {
341 return this->query<Ret, Args...>(
query, options(), std::forward<Args>(args)...);
344 template <
typename Ret, convertible_into_nullable_datum... Args>
345 results<Ret>
query(spi_plan<Args...> &
query, options &&opts, Args &&...args) {
346 if (executors.top() !=
this) {
347 throw std::runtime_error(
"not a current SPI executor");
349 constexpr size_t nargs =
sizeof...(Args);
350 std::array<::Datum, nargs> datums = {into_nullable_datum(args)...};
351 std::array<const char, nargs> nulls = {into_nullable_datum(args).is_null() ?
'n' :
' ' ...};
352 auto rc = ffi_guard{::SPI_execute_plan}(
query, datums.data(), nulls.data(), opts.read_only(),
356 return results<Ret>(SPI_tuptable);
358 throw std::runtime_error(
"spi error");
362 template <convertible_into_nullable_datum_and_has_a_type... Args>
363 uint64_t execute(std::string_view
query, Args &&...args) {
364 return execute(
query, options(), std::forward<Args>(args)...);
367 template <convertible_into_nullable_datum_and_has_a_type... Args>
368 uint64_t execute(std::string_view
query, options &&opts, Args &&...args) {
369 if (executors.top() !=
this) {
370 throw std::runtime_error(
"not a current SPI executor");
372 constexpr size_t nargs =
sizeof...(Args);
373 std::array<::Oid, nargs> types = {type_traits<Args>(args...).type_for().oid...};
374 std::array<::Datum, nargs> datums = {into_nullable_datum(args)...};
375 std::array<const char, nargs> nulls = {into_nullable_datum(args).is_null() ?
'n' :
' ' ...};
376 auto rc = ffi_guard{::SPI_execute_with_args}(
query.data(), nargs, types.data(), datums.data(),
377 nulls.data(), opts.read_only(), opts.count());
379 return SPI_processed;
381 throw std::runtime_error(cppgres::fmt::format(
"spi error"));
386 ::MemoryContext before_spi;
390 static inline std::stack<spi_executor *> executors;
391 spi_executor(
int flags) : before_spi(::CurrentMemoryContext) {
392 ffi_guard{::SPI_connect_ext}(flags);
393 spi = ::CurrentMemoryContext;
394 ::CurrentMemoryContext = before_spi;
395 executors.push(
this);
403 auto atomic = cppgres::current_postgres_function::atomic();
404 if (atomic.has_value() && atomic.value()) {
405 throw std::runtime_error(
"must be called in a non-atomic context");
409 void commit(
bool chain =
false) {
410 if (executors.top() !=
this) {
411 throw std::runtime_error(
"not a current SPI executor");
413 ffi_guard(chain ? ::SPI_commit_and_chain : ::SPI_commit)();
416 void rollback(
bool chain =
false) {
417 if (executors.top() !=
this) {
418 throw std::runtime_error(
"not a current SPI executor");
420 ffi_guard(chain ? ::SPI_rollback_and_chain : ::SPI_rollback)();
Definition: executor.hpp:59
Definition: datum.hpp:138
Definition: datum.hpp:184
Definition: executor.hpp:21
Definition: datum.hpp:192
Definition: cstring.hpp:21
Definition: executor.hpp:56
Definition: memory.hpp:61
Definition: memory.hpp:243
Definition: executor.hpp:263
Definition: executor.hpp:77
Definition: executor.hpp:215
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:309
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:292
Definition: executor.hpp:399
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