First Commit
Some checks failed
/ build_macos (push) Has been cancelled
/ build_windows (push) Has been cancelled
/ build_ubuntu (push) Has been cancelled

This commit is contained in:
2025-11-19 16:23:45 +07:00
commit dbdc5bcc4a
1791 changed files with 489451 additions and 0 deletions

View File

@@ -0,0 +1,305 @@
#if !defined(PQXX_ARRAY_COMPOSITE_HXX)
# define PQXX_ARRAY_COMPOSITE_HXX
# include <cassert>
# include "pqxx/strconv.hxx"
namespace pqxx::internal
{
// Find the end of a double-quoted string.
/** `input[pos]` must be the opening double quote.
*
* Returns the offset of the first position after the closing quote.
*/
inline std::size_t scan_double_quoted_string(
char const input[], std::size_t size, std::size_t pos,
pqxx::internal::glyph_scanner_func *scan)
{
// XXX: find_char<'"', '\\'>().
auto next{scan(input, size, pos)};
bool at_quote{false};
for (pos = next, next = scan(input, size, pos); pos < size;
pos = next, next = scan(input, size, pos))
{
if (at_quote)
{
if (next - pos == 1 and input[pos] == '"')
{
// We just read a pair of double quotes. Carry on.
at_quote = false;
}
else
{
// We just read one double quote, and now we're at a character that's
// not a second double quote. Ergo, that last character was the
// closing double quote and this is the position right after it.
return pos;
}
}
else if (next - pos == 1)
{
switch (input[pos])
{
case '\\':
// Backslash escape. Skip ahead by one more character.
pos = next;
next = scan(input, size, pos);
break;
case '"':
// This is either the closing double quote, or the first of a pair of
// double quotes.
at_quote = true;
break;
}
}
else
{
// Multibyte character. Carry on.
}
}
if (not at_quote)
throw argument_error{
"Missing closing double-quote: " + std::string{input}};
return pos;
}
/// Un-quote and un-escape a double-quoted SQL string.
inline std::string parse_double_quoted_string(
char const input[], std::size_t end, std::size_t pos,
pqxx::internal::glyph_scanner_func *scan)
{
std::string output;
// Maximum output size is same as the input size, minus the opening and
// closing quotes. Or in the extreme opposite case, the real number could be
// half that. Usually it'll be a pretty close estimate.
output.reserve(std::size_t(end - pos - 2));
for (auto here{scan(input, end, pos)}, next{scan(input, end, here)};
here < end - 1; here = next, next = scan(input, end, here))
{
// A backslash here is always an escape. So is a double-quote, since we're
// inside the double-quoted string. In either case, we can just ignore the
// escape character and use the next character. This is the one redeeming
// feature of SQL's escaping system.
if ((next - here == 1) and (input[here] == '\\' or input[here] == '"'))
{
// Skip escape.
here = next;
next = scan(input, end, here);
}
output.append(input + here, input + next);
}
return output;
}
/// Find the end of an unquoted string in an array or composite-type value.
/** Stops when it gets to the end of the input; or when it sees any of the
* characters in STOP which has not been escaped.
*
* For array values, STOP is a comma, a semicolon, or a closing brace. For
* a value of a composite type, STOP is a comma or a closing parenthesis.
*/
template<char... STOP>
inline std::size_t scan_unquoted_string(
char const input[], std::size_t size, std::size_t pos,
pqxx::internal::glyph_scanner_func *scan)
{
bool at_backslash{false};
auto next{scan(input, size, pos)};
while ((pos < size) and
((next - pos) > 1 or at_backslash or ((input[pos] != STOP) and ...)))
{
pos = next;
next = scan(input, size, pos);
at_backslash =
((not at_backslash) and ((next - pos) == 1) and (input[pos] == '\\'));
}
return pos;
}
/// Parse an unquoted array entry or cfield of a composite-type field.
inline std::string parse_unquoted_string(
char const input[], std::size_t end, std::size_t pos,
pqxx::internal::glyph_scanner_func *scan)
{
std::string output;
bool at_backslash{false};
output.reserve(end - pos);
for (auto next{scan(input, end, pos)}; pos < end;
pos = next, next = scan(input, end, pos))
{
at_backslash =
((not at_backslash) and ((next - pos) == 1) and (input[pos] == '\\'));
if (not at_backslash)
output.append(input + pos, next - pos);
}
return output;
}
/// Parse a field of a composite-type value.
/** `T` is the C++ type of the field we're parsing, and `index` is its
* zero-based number.
*
* Strip off the leading parenthesis or bracket yourself before parsing.
* However, this function will parse the lcosing parenthesis or bracket.
*
* After a successful parse, `pos` will point at `std::end(text)`.
*
* For the purposes of parsing, ranges and arrays count as compositve values,
* so this function supports parsing those. If you specifically need a closing
* parenthesis, check afterwards that `text` did not end in a bracket instead.
*
* @param index Index of the current field, zero-based. It will increment for
* the next field.
* @param input Full input text for the entire composite-type value.
* @param pos Starting position (in `input`) of the field that we're parsing.
* After parsing, this will point at the beginning of the next field if
* there is one, or one position past the last character otherwise.
* @param field Destination for the parsed value.
* @param scan Glyph scanning function for the relevant encoding type.
* @param last_field Number of the last field in the value (zero-based). When
* parsing the last field, this will equal `index`.
*/
template<typename T>
inline void parse_composite_field(
std::size_t &index, std::string_view input, std::size_t &pos, T &field,
glyph_scanner_func *scan, std::size_t last_field)
{
assert(index <= last_field);
auto next{scan(std::data(input), std::size(input), pos)};
if ((next - pos) != 1)
throw conversion_error{"Non-ASCII character in composite-type syntax."};
// Expect a field.
switch (input[pos])
{
case ',':
case ')':
case ']':
// The field is empty, i.e, null.
if constexpr (nullness<T>::has_null)
field = nullness<T>::null();
else
throw conversion_error{
"Can't read composite field " + to_string(index) + ": C++ type " +
type_name<T> + " does not support nulls."};
break;
case '"': {
auto const stop{scan_double_quoted_string(
std::data(input), std::size(input), pos, scan)};
auto const text{
parse_double_quoted_string(std::data(input), stop, pos, scan)};
field = from_string<T>(text);
pos = stop;
}
break;
default: {
auto const stop{scan_unquoted_string<',', ')', ']'>(
std::data(input), std::size(input), pos, scan)};
auto const text{parse_unquoted_string(std::data(input), stop, pos, scan)};
field = from_string<T>(text);
pos = stop;
}
break;
}
// Expect a comma or a closing parenthesis.
next = scan(std::data(input), std::size(input), pos);
if ((next - pos) != 1)
throw conversion_error{
"Unexpected non-ASCII character after composite field: " +
std::string{input}};
if (index < last_field)
{
if (input[pos] != ',')
throw conversion_error{
"Found '" + std::string{input[pos]} +
"' in composite value where comma was expected: " + std::data(input)};
}
else
{
if (input[pos] == ',')
throw conversion_error{
"Composite value contained more fields than the expected " +
to_string(last_field) + ": " + std::data(input)};
if (input[pos] != ')' and input[pos] != ']')
throw conversion_error{
"Composite value has unexpected characters where closing parenthesis "
"was expected: " +
std::string{input}};
if (next != std::size(input))
throw conversion_error{
"Composite value has unexpected text after closing parenthesis: " +
std::string{input}};
}
pos = next;
++index;
}
/// Conservatively estimate buffer size needed for a composite field.
template<typename T>
inline std::size_t size_composite_field_buffer(T const &field)
{
if constexpr (is_unquoted_safe<T>)
{
// Safe to copy, without quotes or escaping. Drop the terminating zero.
return size_buffer(field) - 1;
}
else
{
// + Opening quote.
// + Field budget.
// - Terminating zero.
// + Escaping for each byte in the field's string representation.
// - Escaping for terminating zero.
// + Closing quote.
return 1 + 2 * (size_buffer(field) - 1) + 1;
}
}
template<typename T>
inline void write_composite_field(char *&pos, char *end, T const &field)
{
if constexpr (is_unquoted_safe<T>)
{
// No need for quoting or escaping. Convert it straight into its final
// place in the buffer, and "backspace" the trailing zero.
pos = string_traits<T>::into_buf(pos, end, field) - 1;
}
else
{
// The field may need escaping, which means we need an intermediate buffer.
// To avoid allocating that at run time, we use the end of the buffer that
// we have.
auto const budget{size_buffer(field)};
*pos++ = '"';
// Now escape buf into its final position.
for (char const c : string_traits<T>::to_buf(end - budget, end, field))
{
if ((c == '"') or (c == '\\'))
*pos++ = '\\';
*pos++ = c;
}
*pos++ = '"';
}
*pos++ = ',';
}
} // namespace pqxx::internal
#endif

View File

@@ -0,0 +1,70 @@
#ifndef PQXX_H_CALLGATE
#define PQXX_H_CALLGATE
/*
Here's what a typical gate class definition looks like:
#include <pqxx/internal/callgate.hxx>
namespace pqxx::internal::gate
{
class PQXX_PRIVATE @gateclass@ : callgate<@host@>
{
friend class @client@;
@gateclass@(reference x) : super(x) {}
// Methods here. Use home() to access the host-class object.
};
} // namespace pqxx::internal::gate
*/
namespace pqxx::internal
{
/// Base class for call gates.
/**
* A call gate defines a limited, private interface on the host class that
* specified client classes can access.
*
* The metaphor works as follows: the gate stands in front of a "home," which
* is really a class, and only lets specific friends in.
*
* To implement a call gate that gives client C access to host H,
* * derive a gate class from callgate<H>;
* * make the gate class a friend of H;
* * make C a friend of the gate class; and
* * implement "stuff C can do with H" as private members in the gate class.
*
* This special kind of "gated" friendship gives C private access to H, but
* only through an expressly limited interface. The gate class can access its
* host object as home().
*
* Keep gate classes entirely stateless. They should be ultra-lightweight
* wrappers for their host classes, and be optimized away as much as possible
* by the compiler. Once you start adding state, you're on a slippery slope
* away from the pure, clean, limited interface pattern that gate classes are
* meant to implement.
*
* Ideally, all member functions of the gate class should be one-liners passing
* calls straight on to the host class. It can be useful however to break this
* rule temporarily during inter-class refactoring.
*/
template<typename HOME> class PQXX_PRIVATE callgate
{
protected:
/// This class, to keep constructors easy.
using super = callgate<HOME>;
/// A reference to the host class. Helps keep constructors easy.
using reference = HOME &;
callgate(reference x) : m_home(x) {}
/// The home object. The gate class has full "private" access.
reference home() const noexcept { return m_home; }
private:
reference m_home;
};
} // namespace pqxx::internal
#endif

View File

@@ -0,0 +1,45 @@
#if !defined(PQXX_CONCAT_HXX)
# define PQXX_CONCAT_HXX
# include <string>
# include <string_view>
# include "pqxx/strconv.hxx"
namespace pqxx::internal
{
/// Convert item to a string, write it into [here, end).
template<typename TYPE>
void render_item(TYPE const &item, char *&here, char *end)
{
here = string_traits<TYPE>::into_buf(here, end, item) - 1;
}
// C++20: Support non-random_access_range ranges.
/// Efficiently combine a bunch of items into one big string.
/** Use this as an optimised version of string concatentation. It takes just
* about any type; it will represent each item as a string according to its
* @ref string_traits.
*
* This is a simpler, more specialised version of @ref separated_list for a
* statically known series of items, possibly of different types.
*/
template<typename... TYPE>
[[nodiscard]] inline std::string concat(TYPE... item)
{
std::string buf;
// Size to accommodate string representations of all inputs, minus their
// terminating zero bytes.
buf.resize(size_buffer(item...));
char *const data{buf.data()};
char *here = data;
char *end = data + std::size(buf);
(render_item(item, here, end), ...);
buf.resize(static_cast<std::size_t>(here - data));
return buf;
}
} // namespace pqxx::internal
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,60 @@
/** Enum type for supporting encodings in libpqxx
*
* Copyright (c) 2000-2022, Jeroen T. Vermeulen.
*
* See COPYING for copyright license. If you did not receive a file called
* COPYING with this source code, please notify the distributor of this
* mistake, or contact the author.
*/
#ifndef PQXX_H_ENCODING_GROUP
#define PQXX_H_ENCODING_GROUP
#include <cstddef>
namespace pqxx::internal
{
// Types of encodings supported by PostgreSQL, see
// https://www.postgresql.org/docs/current/static/multibyte.html#CHARSET-TABLE
enum class encoding_group
{
// Handles all single-byte fixed-width encodings
MONOBYTE,
// Multibyte encodings.
// Many of these can embed ASCII-like bytes inside multibyte characters,
// notably Big5, SJIS, SHIFT_JIS_2004, GP18030, GBK, JOHAB, UHC.
BIG5,
EUC_CN,
// TODO: Merge EUC_JP and EUC_JIS_2004?
EUC_JP,
EUC_JIS_2004,
EUC_KR,
EUC_TW,
GB18030,
GBK,
JOHAB,
MULE_INTERNAL,
// TODO: Merge SJIS and SHIFT_JIS_2004?
SJIS,
SHIFT_JIS_2004,
UHC,
UTF8,
};
// TODO:: Can we just use string_view now?
/// Function type: "find the end of the current glyph."
/** This type of function takes a text buffer, and a location in that buffer,
* and returns the location one byte past the end of the current glyph.
*
* The start offset marks the beginning of the current glyph. It must fall
* within the buffer.
*
* There are multiple different glyph scanner implementations, for different
* kinds of encodings.
*/
using glyph_scanner_func =
std::size_t(char const buffer[], std::size_t buffer_len, std::size_t start);
} // namespace pqxx::internal
#endif

View File

@@ -0,0 +1,90 @@
/** Internal string encodings support for libpqxx
*
* Copyright (c) 2000-2022, Jeroen T. Vermeulen.
*
* See COPYING for copyright license. If you did not receive a file called
* COPYING with this source code, please notify the distributor of this
* mistake, or contact the author.
*/
#ifndef PQXX_H_ENCODINGS
#define PQXX_H_ENCODINGS
#include "pqxx/internal/encoding_group.hxx"
#include <string>
#include <string_view>
namespace pqxx::internal
{
char const *name_encoding(int encoding_id);
/// Convert libpq encoding enum or encoding name to its libpqxx group.
encoding_group enc_group(int /* libpq encoding ID */);
encoding_group enc_group(std::string_view);
/// Look up the glyph scanner function for a given encoding group.
/** To identify the glyph boundaries in a buffer, call this to obtain the
* scanner function appropriate for the buffer's encoding. Then, repeatedly
* call the scanner function to find the glyphs.
*/
PQXX_LIBEXPORT glyph_scanner_func *get_glyph_scanner(encoding_group);
// TODO: For ASCII search, treat UTF8/EUC_*/MULE_INTERNAL as MONOBYTE.
/// Find any of the ASCII characters `NEEDLE` in `haystack`.
/** Scans through `haystack` until it finds a single-byte character that
* matches any value in `NEEDLE`.
*
* If it finds one, returns its offset. If not, returns the end of the
* haystack.
*/
template<char... NEEDLE>
inline std::size_t find_char(
glyph_scanner_func *scanner, std::string_view haystack,
std::size_t here = 0u)
{
auto const sz{std::size(haystack)};
auto const data{std::data(haystack)};
while (here < sz)
{
auto next{scanner(data, sz, here)};
// (For some reason gcc had a problem with a right-fold here. But clang
// was fine.)
if ((... or (data[here] == NEEDLE)))
{
// Also check against a multibyte character starting with a bytes which
// just happens to match one of the ASCII bytes we're looking for. It'd
// be cleaner to check that first, but either works. So, let's apply the
// most selective filter first and skip this check in almost all cases.
if (next == here + 1)
return here;
}
// Nope, no hit. Move on.
here = next;
}
return sz;
}
/// Iterate over the glyphs in a buffer.
/** Scans the glyphs in the buffer, and for each, passes its begin and its
* one-past-end pointers to `callback`.
*/
template<typename CALLABLE>
inline void for_glyphs(
encoding_group enc, CALLABLE callback, char const buffer[],
std::size_t buffer_len, std::size_t start = 0)
{
auto const scan{get_glyph_scanner(enc)};
for (std::size_t here = start, next; here < buffer_len; here = next)
{
next = scan(buffer, buffer_len, here);
callback(buffer + here, buffer + next);
}
}
} // namespace pqxx::internal
#endif

View File

@@ -0,0 +1,26 @@
#include <pqxx/internal/callgate.hxx>
namespace pqxx
{
class connection;
class errorhandler;
} // namespace pqxx
namespace pqxx::internal::gate
{
class PQXX_PRIVATE connection_errorhandler : callgate<connection>
{
friend class pqxx::errorhandler;
connection_errorhandler(reference x) : super(x) {}
void register_errorhandler(errorhandler *h)
{
home().register_errorhandler(h);
}
void unregister_errorhandler(errorhandler *h)
{
home().unregister_errorhandler(h);
}
};
} // namespace pqxx::internal::gate

View File

@@ -0,0 +1,35 @@
#include <string>
#include <pqxx/internal/callgate.hxx>
#include <pqxx/internal/libpq-forward.hxx>
namespace pqxx
{
class blob;
class largeobject;
} // namespace pqxx
namespace pqxx::internal::gate
{
class PQXX_PRIVATE connection_largeobject : callgate<connection>
{
friend class pqxx::blob;
friend class pqxx::largeobject;
connection_largeobject(reference x) : super(x) {}
pq::PGconn *raw_connection() const { return home().raw_connection(); }
};
class PQXX_PRIVATE const_connection_largeobject : callgate<connection const>
{
friend class pqxx::blob;
friend class pqxx::largeobject;
const_connection_largeobject(reference x) : super(x) {}
std::string error_message() const { return home().err_msg(); }
};
} // namespace pqxx::internal::gate

View File

@@ -0,0 +1,29 @@
#include <pqxx/internal/callgate.hxx>
#include "pqxx/connection.hxx"
namespace pqxx
{
class notification_receiver;
}
namespace pqxx::internal::gate
{
class PQXX_PRIVATE connection_notification_receiver : callgate<connection>
{
friend class pqxx::notification_receiver;
connection_notification_receiver(reference x) : super(x) {}
void add_receiver(notification_receiver *receiver)
{
home().add_receiver(receiver);
}
void remove_receiver(notification_receiver *receiver) noexcept
{
home().remove_receiver(receiver);
}
};
} // namespace pqxx::internal::gate

View File

@@ -0,0 +1,23 @@
#include "pqxx/internal/libpq-forward.hxx"
#include <pqxx/internal/callgate.hxx>
#include "pqxx/pipeline.hxx"
namespace pqxx::internal::gate
{
class PQXX_PRIVATE connection_pipeline : callgate<connection>
{
friend class pqxx::pipeline;
connection_pipeline(reference x) : super(x) {}
void start_exec(char const query[]) { home().start_exec(query); }
pqxx::internal::pq::PGresult *get_result() { return home().get_result(); }
void cancel_query() { home().cancel_query(); }
bool consume_input() noexcept { return home().consume_input(); }
bool is_busy() const noexcept { return home().is_busy(); }
int encoding_id() { return home().encoding_id(); }
};
} // namespace pqxx::internal::gate

View File

@@ -0,0 +1,19 @@
#include <pqxx/internal/callgate.hxx>
namespace pqxx::internal
{
class sql_cursor;
}
namespace pqxx::internal::gate
{
class PQXX_PRIVATE connection_sql_cursor : callgate<connection>
{
friend class pqxx::internal::sql_cursor;
connection_sql_cursor(reference x) : super(x) {}
result exec(char const query[]) { return home().exec(query); }
};
} // namespace pqxx::internal::gate

View File

@@ -0,0 +1,15 @@
#include <pqxx/internal/callgate.hxx>
#include "pqxx/connection.hxx"
namespace pqxx::internal::gate
{
class PQXX_PRIVATE connection_stream_from : callgate<connection>
{
friend class pqxx::stream_from;
connection_stream_from(reference x) : super{x} {}
auto read_copy_line() { return home().read_copy_line(); }
};
} // namespace pqxx::internal::gate

View File

@@ -0,0 +1,17 @@
#include <pqxx/internal/callgate.hxx>
#include "pqxx/stream_to.hxx"
namespace pqxx::internal::gate
{
class PQXX_PRIVATE connection_stream_to : callgate<connection>
{
friend class pqxx::stream_to;
connection_stream_to(reference x) : super(x) {}
void write_copy_line(std::string_view line) { home().write_copy_line(line); }
void end_copy_write() { home().end_copy_write(); }
};
} // namespace pqxx::internal::gate

View File

@@ -0,0 +1,44 @@
#include <pqxx/internal/callgate.hxx>
namespace pqxx
{
class connection;
}
namespace pqxx::internal::gate
{
class PQXX_PRIVATE connection_transaction : callgate<connection>
{
friend class pqxx::transaction_base;
connection_transaction(reference x) : super(x) {}
template<typename STRING> result exec(STRING query, std::string_view desc)
{
return home().exec(query, desc);
}
void register_transaction(transaction_base *t)
{
home().register_transaction(t);
}
void unregister_transaction(transaction_base *t) noexcept
{
home().unregister_transaction(t);
}
auto read_copy_line() { return home().read_copy_line(); }
void write_copy_line(std::string_view line) { home().write_copy_line(line); }
void end_copy_write() { home().end_copy_write(); }
result exec_prepared(zview statement, internal::c_params const &args)
{
return home().exec_prepared(statement, args);
}
result exec_params(zview query, internal::c_params const &args)
{
return home().exec_params(query, args);
}
};
} // namespace pqxx::internal::gate

View File

@@ -0,0 +1,13 @@
#include <pqxx/internal/callgate.hxx>
namespace pqxx::internal::gate
{
class PQXX_PRIVATE errorhandler_connection : callgate<errorhandler>
{
friend class pqxx::connection;
errorhandler_connection(reference x) : super(x) {}
void unregister() noexcept { home().unregister(); }
};
} // namespace pqxx::internal::gate

View File

@@ -0,0 +1,24 @@
#include <pqxx/internal/callgate.hxx>
namespace pqxx::internal::gate
{
class PQXX_PRIVATE icursor_iterator_icursorstream : callgate<icursor_iterator>
{
friend class pqxx::icursorstream;
icursor_iterator_icursorstream(reference x) : super(x) {}
icursor_iterator::difference_type pos() const noexcept
{
return home().pos();
}
icursor_iterator *get_prev() { return home().m_prev; }
void set_prev(icursor_iterator *i) { home().m_prev = i; }
icursor_iterator *get_next() { return home().m_next; }
void set_next(icursor_iterator *i) { home().m_next = i; }
void fill(result const &r) { home().fill(r); }
};
} // namespace pqxx::internal::gate

View File

@@ -0,0 +1,32 @@
#include <pqxx/internal/callgate.hxx>
namespace pqxx::internal::gate
{
class PQXX_PRIVATE icursorstream_icursor_iterator : callgate<icursorstream>
{
friend class pqxx::icursor_iterator;
icursorstream_icursor_iterator(reference x) : super(x) {}
void insert_iterator(icursor_iterator *i) noexcept
{
home().insert_iterator(i);
}
void remove_iterator(icursor_iterator *i) const noexcept
{
home().remove_iterator(i);
}
icursorstream::size_type forward() { return home().forward(); }
icursorstream::size_type forward(icursorstream::size_type n)
{
return home().forward(n);
}
void service_iterators(icursorstream::difference_type p)
{
home().service_iterators(p);
}
};
} // namespace pqxx::internal::gate

View File

@@ -0,0 +1,14 @@
#include <pqxx/internal/callgate.hxx>
namespace pqxx::internal::gate
{
class PQXX_PRIVATE result_connection : callgate<result const>
{
friend class pqxx::connection;
result_connection(reference x) : super(x) {}
operator bool() const { return bool(home()); }
bool operator!() const { return not home(); }
};
} // namespace pqxx::internal::gate

View File

@@ -0,0 +1,24 @@
#include <pqxx/internal/callgate.hxx>
namespace pqxx::internal::gate
{
class PQXX_PRIVATE result_creation : callgate<result const>
{
friend class pqxx::connection;
friend class pqxx::pipeline;
result_creation(reference x) : super(x) {}
static result create(
internal::pq::PGresult *rhs, std::shared_ptr<std::string> const &query,
encoding_group enc)
{
return result(rhs, query, enc);
}
void check_status(std::string_view desc = ""sv) const
{
return home().check_status(desc);
}
};
} // namespace pqxx::internal::gate

View File

@@ -0,0 +1,16 @@
#include <pqxx/internal/callgate.hxx>
namespace pqxx::internal::gate
{
class PQXX_PRIVATE result_pipeline : callgate<result const>
{
friend class pqxx::pipeline;
result_pipeline(reference x) : super(x) {}
std::shared_ptr<std::string const> query_ptr() const
{
return home().query_ptr();
}
};
} // namespace pqxx::internal::gate

View File

@@ -0,0 +1,13 @@
#include <pqxx/internal/callgate.hxx>
namespace pqxx::internal::gate
{
class PQXX_PRIVATE result_sql_cursor : callgate<result const>
{
friend class pqxx::internal::sql_cursor;
result_sql_cursor(reference x) : super(x) {}
char const *cmd_status() const noexcept { return home().cmd_status(); }
};
} // namespace pqxx::internal::gate

View File

@@ -0,0 +1,10 @@
#include <pqxx/internal/callgate.hxx>
namespace pqxx::internal::gate
{
class PQXX_PRIVATE transaction_sql_cursor : callgate<transaction_base>
{
friend class pqxx::internal::sql_cursor;
transaction_sql_cursor(reference x) : super(x) {}
};
} // namespace pqxx::internal::gate

View File

@@ -0,0 +1,30 @@
#include <pqxx/internal/callgate.hxx>
#include "pqxx/transaction_base.hxx"
namespace pqxx::internal::gate
{
class PQXX_PRIVATE transaction_transaction_focus : callgate<transaction_base>
{
friend class pqxx::transaction_focus;
transaction_transaction_focus(reference x) : super(x) {}
void register_focus(transaction_focus *focus)
{
home().register_focus(focus);
}
void unregister_focus(transaction_focus *focus) noexcept
{
home().unregister_focus(focus);
}
void register_pending_error(zview error)
{
home().register_pending_error(error);
}
void register_pending_error(std::string &&error)
{
home().register_pending_error(std::move(error));
}
};
} // namespace pqxx::internal::gate

View File

@@ -0,0 +1,22 @@
/* Compiler deficiency workarounds for compiling libpqxx headers.
*
* To be included at the end of each libpqxx header, in order to restore the
* client program's settings.
*
* Copyright (c) 2000-2022, Jeroen T. Vermeulen.
*
* See COPYING for copyright license. If you did not receive a file called
* COPYING with this source code, please notify the distributor of this
* mistake, or contact the author.
*/
// NO GUARDS HERE! This code should be executed every time!
#if defined(_MSC_VER)
# pragma warning(pop) // Restore compiler's warning state
#endif
#if !defined(PQXX_HEADER_PRE)
# error "Include pqxx/internal/header-post.hxx AFTER its 'pre' counterpart."
#endif
#undef PQXX_HEADER_PRE

View File

@@ -0,0 +1,169 @@
/* Compiler settings for compiling libpqxx headers, and workarounds for all.
*
* Include this before including any other libpqxx headers from within libpqxx.
* And to balance it out, also include header-post.hxx at the end of the batch
* of headers.
*
* The public libpqxx headers (e.g. `<pqxx/connection>`) include this already;
* there's no need to do this from within an application.
*
* Include this file at the highest aggregation level possible to avoid nesting
* and to keep things simple.
*
* Copyright (c) 2000-2022, Jeroen T. Vermeulen.
*
* See COPYING for copyright license. If you did not receive a file called
* COPYING with this source code, please notify the distributor of this
* mistake, or contact the author.
*/
// NO GUARD HERE! This part should be included every time this file is.
#if defined(_MSC_VER)
// Save compiler's warning state, and set warning level 4 for maximum
// sensitivity to warnings.
# pragma warning(push, 4)
// Visual C++ generates some entirely unreasonable warnings. Disable them.
// Copy constructor could not be generated.
# pragma warning(disable : 4511)
// Assignment operator could not be generated.
# pragma warning(disable : 4512)
// Can't expose outside classes without exporting them. Except the MSVC docs
// say please ignore the warning if it's a standard library class.
# pragma warning(disable : 4251)
// Can't derive library classes from outside classes without exporting them.
// Except the MSVC docs say please ignore the warning if the parent class is
// in the standard library.
# pragma warning(disable : 4275)
// Can't inherit from non-exported class.
# pragma warning(disable : 4275)
#endif // _MSC_VER
#if defined(PQXX_HEADER_PRE)
# error "Avoid nesting #include of pqxx/internal/header-pre.hxx."
#endif
#define PQXX_HEADER_PRE
// Workarounds & definitions that need to be included even in library's headers
#include "pqxx/config-public-compiler.h"
// Enable ISO-646 alternative operaotr representations: "and" instead of "&&"
// etc. on older compilers. C++20 removes this header.
#if __has_include(<ciso646>)
# include <ciso646>
#endif
#if defined(PQXX_HAVE_GCC_PURE)
/// Declare function "pure": no side effects, only reads globals and its args.
# define PQXX_PURE __attribute__((pure))
#else
# define PQXX_PURE /* pure */
#endif
#if defined(__GNUC__)
/// Tell the compiler to optimise a function for size, not speed.
# define PQXX_COLD __attribute__((cold))
#else
# define PQXX_COLD /* cold */
#endif
// Workarounds for Windows
#ifdef _WIN32
/* For now, export DLL symbols if _DLL is defined. This is done automatically
* by the compiler when linking to the dynamic version of the runtime library,
* according to "gzh"
*/
# if defined(PQXX_SHARED) && !defined(PQXX_LIBEXPORT)
# define PQXX_LIBEXPORT __declspec(dllimport)
# endif // PQXX_SHARED && !PQXX_LIBEXPORT
// Workarounds for Microsoft Visual C++
# ifdef _MSC_VER
// Suppress vtables on abstract classes.
# define PQXX_NOVTABLE __declspec(novtable)
// Automatically link with the appropriate libpq (static or dynamic, debug or
// release). The default is to use the release DLL. Define PQXX_PQ_STATIC to
// link to a static version of libpq, and _DEBUG to link to a debug version.
// The two may be combined.
# if defined(PQXX_AUTOLINK)
# if defined(PQXX_PQ_STATIC)
# ifdef _DEBUG
# pragma comment(lib, "libpqd")
# else
# pragma comment(lib, "libpq")
# endif
# else
# ifdef _DEBUG
# pragma comment(lib, "libpqddll")
# else
# pragma comment(lib, "libpqdll")
# endif
# endif
# endif
// If we're not compiling libpqxx itself, automatically link with the
// appropriate libpqxx library. To link with the libpqxx DLL, define
// PQXX_SHARED; the default is to link with the static library. A static link
// is the recommended practice.
//
// The preprocessor macro PQXX_INTERNAL is used to detect whether we
// are compiling the libpqxx library itself. When you compile the library
// yourself using your own project file, make sure to include this macro.
# if defined(PQXX_AUTOLINK) && !defined(PQXX_INTERNAL)
# ifdef PQXX_SHARED
# ifdef _DEBUG
# pragma comment(lib, "libpqxxD")
# else
# pragma comment(lib, "libpqxx")
# endif
# else // !PQXX_SHARED
# ifdef _DEBUG
# pragma comment(lib, "libpqxx_staticD")
# else
# pragma comment(lib, "libpqxx_static")
# endif
# endif
# endif
# endif // _MSC_VER
#elif defined(PQXX_HAVE_GCC_VISIBILITY) // !_WIN32
# define PQXX_LIBEXPORT __attribute__((visibility("default")))
# define PQXX_PRIVATE __attribute__((visibility("hidden")))
#endif // PQXX_HAVE_GCC_VISIBILITY
#ifndef PQXX_LIBEXPORT
# define PQXX_LIBEXPORT /* libexport */
#endif
#ifndef PQXX_PRIVATE
# define PQXX_PRIVATE /* private */
#endif
#ifndef PQXX_NOVTABLE
# define PQXX_NOVTABLE /* novtable */
#endif
// C++20: Assume support.
#if defined(PQXX_HAVE_LIKELY)
# define PQXX_LIKELY [[likely]]
# define PQXX_UNLIKELY [[unlikely]]
#else
# define PQXX_LIKELY /* [[likely]] */
# define PQXX_UNLIKELY /* [[unlikely]] */
#endif

View File

@@ -0,0 +1,15 @@
/// End a code block started by "ignore-deprecated-pre.hxx".
#if !defined(PQXX_IGNORING_DEPRECATED)
# error "Ended an 'ignore-deprecated' block while none was active."
#endif
#if defined(__GNUC__)
# pragma GCC diagnostic pop
#endif // __GNUC__
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#undef PQXX_IGNORING_DEPRECATED

View File

@@ -0,0 +1,28 @@
/** Start a block of deprecated code which may call other deprecated code.
*
* Most compilers will emit warnings when deprecated code is invoked from
* non-deprecated code. But some compilers (notably gcc) will always emit the
* warning even when the calling code is also deprecated.
*
* This header starts a block where those warnings are suppressed. It can be
* included inside a code block.
*
* Always match the #include with a closing #include of
* "ignore-deprecated-post.hxx". To avoid mistakes, keep the enclosed area as
* small as possible.
*/
#if defined(PQXX_IGNORING_DEPRECATED)
# error "Started an 'ignore-deprecated' block inside another."
#endif
#define PQXX_IGNORING_DEPRECATED
#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif // __GNUC__
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable : 4996)
#endif

View File

@@ -0,0 +1,31 @@
/** Minimal forward declarations of libpq types needed in libpqxx headers.
*
* DO NOT INCLUDE THIS FILE when building client programs.
*
* Copyright (c) 2000-2022, Jeroen T. Vermeulen.
*
* See COPYING for copyright license. If you did not receive a file called
* COPYING with this source code, please notify the distributor of this
* mistake, or contact the author.
*/
extern "C"
{
struct pg_conn;
struct pg_result;
struct pgNotify;
}
/// Forward declarations of libpq types as needed in libpqxx headers.
namespace pqxx::internal::pq
{
using PGconn = pg_conn;
using PGresult = pg_result;
using PGnotify = pgNotify;
using PQnoticeProcessor = void (*)(void *, char const *);
} // namespace pqxx::internal::pq
namespace pqxx
{
/// PostgreSQL database row identifier.
using oid = unsigned int;
} // namespace pqxx

View File

@@ -0,0 +1,124 @@
/** Result loops.
*
* Copyright (c) 2000-2022, Jeroen T. Vermeulen.
*
* See COPYING for copyright license. If you did not receive a file called
* COPYING with this source code, please notify the distributor of this
* mistake, or contact the author.
*/
#ifndef PQXX_H_RESULT_ITER
#define PQXX_H_RESULT_ITER
#include <memory>
#include "pqxx/strconv.hxx"
namespace pqxx
{
class result;
} // namespace pqxx
namespace pqxx::internal
{
// C++20: Replace with generator?
/// Iterator for looped unpacking of a result.
template<typename... TYPE> class result_iter
{
public:
using value_type = std::tuple<TYPE...>;
/// Construct an "end" iterator.
result_iter() = default;
explicit result_iter(result const &home) :
m_home{&home}, m_size{std::size(home)}
{
if (not std::empty(home))
read();
}
result_iter(result_iter const &) = default;
result_iter &operator++()
{
m_index++;
if (m_index >= m_size)
m_home = nullptr;
else
read();
return *this;
}
/// Comparison only works for comparing to end().
bool operator==(result_iter const &rhs) const
{
return m_home == rhs.m_home;
}
bool operator!=(result_iter const &rhs) const { return not(*this == rhs); }
value_type const &operator*() const { return m_value; }
private:
void read() { (*m_home)[m_index].convert(m_value); }
result const *m_home{nullptr};
result::size_type m_index{0};
result::size_type m_size;
value_type m_value;
};
template<typename... TYPE> class result_iteration
{
public:
using iterator = result_iter<TYPE...>;
explicit result_iteration(result const &home) : m_home{home}
{
constexpr auto tup_size{sizeof...(TYPE)};
if (home.columns() != tup_size)
throw usage_error{internal::concat(
"Tried to extract ", to_string(tup_size),
" field(s) from a result with ", to_string(home.columns()),
" column(s).")};
}
iterator begin() const
{
if (std::size(m_home) == 0)
return end();
else
return iterator{m_home};
}
iterator end() const { return {}; }
private:
pqxx::result const &m_home;
};
} // namespace pqxx::internal
template<typename... TYPE> inline auto pqxx::result::iter() const
{
return pqxx::internal::result_iteration<TYPE...>{*this};
}
template<typename CALLABLE>
inline void pqxx::result::for_each(CALLABLE &&func) const
{
using args_tuple = internal::args_t<decltype(func)>;
constexpr auto sz{std::tuple_size_v<args_tuple>};
static_assert(
sz > 0,
"Callback for for_each must take parameters, one for each column in the "
"result.");
auto const cols{this->columns()};
if (sz != cols)
throw usage_error{internal::concat(
"Callback to for_each takes ", sz, "parameter", (sz == 1) ? "" : "s",
", but result set has ", cols, "field", (cols == 1) ? "" : "s", ".")};
using pass_tuple = pqxx::internal::strip_types_t<args_tuple>;
for (auto const r : *this) std::apply(func, r.as_tuple<pass_tuple>());
}
#endif

View File

@@ -0,0 +1,389 @@
/* Definitions for the pqxx::result class and support classes.
*
* pqxx::result represents the set of result rows from a database query.
*
* DO NOT INCLUDE THIS FILE DIRECTLY; include pqxx/result instead.
*
* Copyright (c) 2000-2022, Jeroen T. Vermeulen.
*
* See COPYING for copyright license. If you did not receive a file called
* COPYING with this source code, please notify the distributor of this
* mistake, or contact the author.
*/
#ifndef PQXX_H_RESULT_ITERATOR
#define PQXX_H_RESULT_ITERATOR
#include "pqxx/row.hxx"
/* Result iterator.
*
* Don't include this header from your own application; it is included for you
* by other libpqxx headers.
*/
namespace pqxx
{
/// Iterator for rows in a result. Use as result::const_iterator.
/** A result, once obtained, cannot be modified. Therefore there is no
* plain iterator type for result. However its const_iterator type can be
* used to inspect its rows without changing them.
*/
class PQXX_LIBEXPORT const_result_iterator : public row
{
public:
using iterator_category = std::random_access_iterator_tag;
using value_type = row const;
using pointer = row const *;
using reference = row;
using size_type = result_size_type;
using difference_type = result_difference_type;
#include "pqxx/internal/ignore-deprecated-pre.hxx"
/// Create an iterator, but in an unusable state.
const_result_iterator() noexcept = default;
/// Copy an iterator.
const_result_iterator(const_result_iterator const &) noexcept = default;
/// Move an iterator.
const_result_iterator(const_result_iterator &&) noexcept = default;
/// Begin iterating a @ref row.
const_result_iterator(row const &t) noexcept : row{t} {}
#include "pqxx/internal/ignore-deprecated-post.hxx"
/**
* @name Dereferencing operators
*
* An iterator "points to" its own row, which is also itself. This makes it
* easy to address a @ref result as a two-dimensional container, without
* going through the intermediate step of dereferencing the iterator. It
* makes the interface similar to C pointer/array semantics.
*
* IIRC Alex Stepanov, the inventor of the STL, once remarked that having
* this as standard behaviour for pointers would be useful in some
* algorithms. So even if this makes me look foolish, I would seem to be in
* distinguished company.
*/
//@{
/// Dereference the iterator.
[[nodiscard]] pointer operator->() const { return this; }
#include "pqxx/internal/ignore-deprecated-pre.hxx"
/// Dereference the iterator.
[[nodiscard]] reference operator*() const { return *this; }
#include "pqxx/internal/ignore-deprecated-post.hxx"
//@}
/**
* @name Field access
*/
//@{
using row::back;
using row::front;
using row::operator[];
using row::at;
using row::rownumber;
//@}
/**
* @name Manipulations
*/
//@{
const_result_iterator &operator=(const_result_iterator const &rhs)
{
#include "pqxx/internal/ignore-deprecated-pre.hxx"
row::operator=(rhs);
#include "pqxx/internal/ignore-deprecated-post.hxx"
return *this;
}
const_result_iterator &operator=(const_result_iterator &&rhs)
{
#include "pqxx/internal/ignore-deprecated-pre.hxx"
row::operator=(std::move(rhs));
#include "pqxx/internal/ignore-deprecated-post.hxx"
return *this;
}
const_result_iterator operator++(int);
const_result_iterator &operator++()
{
++m_index;
return *this;
}
const_result_iterator operator--(int);
const_result_iterator &operator--()
{
--m_index;
return *this;
}
const_result_iterator &operator+=(difference_type i)
{
m_index += i;
return *this;
}
const_result_iterator &operator-=(difference_type i)
{
m_index -= i;
return *this;
}
/// Interchange two iterators in an exception-safe manner.
void swap(const_result_iterator &other) noexcept
{
#include "pqxx/internal/ignore-deprecated-pre.hxx"
row::swap(other);
#include "pqxx/internal/ignore-deprecated-post.hxx"
}
//@}
/**
* @name Comparisons
*/
//@{
[[nodiscard]] bool operator==(const_result_iterator const &i) const
{
return m_index == i.m_index;
}
[[nodiscard]] bool operator!=(const_result_iterator const &i) const
{
return m_index != i.m_index;
}
[[nodiscard]] bool operator<(const_result_iterator const &i) const
{
return m_index < i.m_index;
}
[[nodiscard]] bool operator<=(const_result_iterator const &i) const
{
return m_index <= i.m_index;
}
[[nodiscard]] bool operator>(const_result_iterator const &i) const
{
return m_index > i.m_index;
}
[[nodiscard]] bool operator>=(const_result_iterator const &i) const
{
return m_index >= i.m_index;
}
//@}
/**
* @name Arithmetic operators
*/
//@{
[[nodiscard]] inline const_result_iterator operator+(difference_type) const;
friend const_result_iterator
operator+(difference_type, const_result_iterator const &);
[[nodiscard]] inline const_result_iterator operator-(difference_type) const;
[[nodiscard]] inline difference_type
operator-(const_result_iterator const &) const;
//@}
private:
friend class pqxx::result;
const_result_iterator(pqxx::result const *r, result_size_type i) noexcept :
row{*r, i, r->columns()}
{}
};
/// Reverse iterator for result. Use as result::const_reverse_iterator.
class PQXX_LIBEXPORT const_reverse_result_iterator
: private const_result_iterator
{
public:
using super = const_result_iterator;
using iterator_type = const_result_iterator;
using iterator_type::difference_type;
using iterator_type::iterator_category;
using iterator_type::pointer;
using value_type = iterator_type::value_type;
using reference = iterator_type::reference;
/// Create an iterator, but in an unusable state.
const_reverse_result_iterator() = default;
/// Copy an iterator.
const_reverse_result_iterator(const_reverse_result_iterator const &rhs) =
default;
/// Copy a reverse iterator from a regular iterator.
explicit const_reverse_result_iterator(const_result_iterator const &rhs) :
const_result_iterator{rhs}
{
super::operator--();
}
/// Move a regular iterator into a reverse iterator.
explicit const_reverse_result_iterator(const_result_iterator const &&rhs) :
const_result_iterator{std::move(rhs)}
{
super::operator--();
}
/// Return the underlying "regular" iterator (as per standard library).
[[nodiscard]] PQXX_PURE const_result_iterator base() const noexcept;
/**
* @name Dereferencing operators
*/
//@{
/// Dereference iterator.
using const_result_iterator::operator->;
/// Dereference iterator.
using const_result_iterator::operator*;
//@}
/**
* @name Field access
*/
//@{
using const_result_iterator::back;
using const_result_iterator::front;
using const_result_iterator::operator[];
using const_result_iterator::at;
using const_result_iterator::rownumber;
//@}
/**
* @name Manipulations
*/
//@{
const_reverse_result_iterator &
operator=(const_reverse_result_iterator const &r)
{
iterator_type::operator=(r);
return *this;
}
const_reverse_result_iterator &operator=(const_reverse_result_iterator &&r)
{
iterator_type::operator=(std::move(r));
return *this;
}
const_reverse_result_iterator &operator++()
{
iterator_type::operator--();
return *this;
}
const_reverse_result_iterator operator++(int);
const_reverse_result_iterator &operator--()
{
iterator_type::operator++();
return *this;
}
const_reverse_result_iterator operator--(int);
const_reverse_result_iterator &operator+=(difference_type i)
{
iterator_type::operator-=(i);
return *this;
}
const_reverse_result_iterator &operator-=(difference_type i)
{
iterator_type::operator+=(i);
return *this;
}
void swap(const_reverse_result_iterator &other) noexcept
{
const_result_iterator::swap(other);
}
//@}
/**
* @name Arithmetic operators
*/
//@{
[[nodiscard]] const_reverse_result_iterator
operator+(difference_type i) const
{
return const_reverse_result_iterator(base() - i);
}
[[nodiscard]] const_reverse_result_iterator operator-(difference_type i)
{
return const_reverse_result_iterator(base() + i);
}
[[nodiscard]] difference_type
operator-(const_reverse_result_iterator const &rhs) const
{
return rhs.const_result_iterator::operator-(*this);
}
//@}
/**
* @name Comparisons
*/
//@{
[[nodiscard]] bool
operator==(const_reverse_result_iterator const &rhs) const noexcept
{
return iterator_type::operator==(rhs);
}
[[nodiscard]] bool
operator!=(const_reverse_result_iterator const &rhs) const noexcept
{
return not operator==(rhs);
}
[[nodiscard]] bool operator<(const_reverse_result_iterator const &rhs) const
{
return iterator_type::operator>(rhs);
}
[[nodiscard]] bool operator<=(const_reverse_result_iterator const &rhs) const
{
return iterator_type::operator>=(rhs);
}
[[nodiscard]] bool operator>(const_reverse_result_iterator const &rhs) const
{
return iterator_type::operator<(rhs);
}
[[nodiscard]] bool operator>=(const_reverse_result_iterator const &rhs) const
{
return iterator_type::operator<=(rhs);
}
//@}
};
inline const_result_iterator
const_result_iterator::operator+(result::difference_type o) const
{
return {&m_result, size_type(result::difference_type(m_index) + o)};
}
inline const_result_iterator
operator+(result::difference_type o, const_result_iterator const &i)
{
return i + o;
}
inline const_result_iterator
const_result_iterator::operator-(result::difference_type o) const
{
return {&m_result, result_size_type(result::difference_type(m_index) - o)};
}
inline result::difference_type
const_result_iterator::operator-(const const_result_iterator &i) const
{
return result::difference_type(num() - i.num());
}
inline const_result_iterator result::end() const noexcept
{
return {this, size()};
}
inline const_result_iterator result::cend() const noexcept
{
return end();
}
inline const_reverse_result_iterator
operator+(result::difference_type n, const_reverse_result_iterator const &i)
{
return const_reverse_result_iterator{i.base() - n};
}
} // namespace pqxx
#endif

View File

@@ -0,0 +1,118 @@
/** Internal wrapper for SQL cursors. Supports higher-level cursor classes.
*
* DO NOT INCLUDE THIS FILE DIRECTLY. Other headers include it for you.
*
* Copyright (c) 2000-2022, Jeroen T. Vermeulen.
*
* See COPYING for copyright license. If you did not receive a file called
* COPYING with this source code, please notify the distributor of this
* mistake, or contact the author.
*/
#ifndef PQXX_H_SQL_CURSOR
#define PQXX_H_SQL_CURSOR
namespace pqxx::internal
{
/// Cursor with SQL positioning semantics.
/** Thin wrapper around an SQL cursor, with SQL's ideas of positioning.
*
* SQL cursors have pre-increment/pre-decrement semantics, with on either end
* of the result set a special position that does not repesent a row. This
* class models SQL cursors for the purpose of implementing more C++-like
* semantics on top.
*
* Positions of actual rows are numbered starting at 1. Position 0 exists but
* does not refer to a row. There is a similar non-row position at the end of
* the result set.
*
* Don't use this at home. You deserve better. Use the stateles_cursor
* instead.
*/
class PQXX_LIBEXPORT sql_cursor : public cursor_base
{
public:
sql_cursor(
transaction_base &t, std::string_view query, std::string_view cname,
cursor_base::access_policy ap, cursor_base::update_policy up,
cursor_base::ownership_policy op, bool hold);
sql_cursor(
transaction_base &t, std::string_view cname,
cursor_base::ownership_policy op);
~sql_cursor() noexcept { close(); }
result fetch(difference_type rows, difference_type &displacement);
result fetch(difference_type rows)
{
difference_type d = 0;
return fetch(rows, d);
}
difference_type move(difference_type rows, difference_type &displacement);
difference_type move(difference_type rows)
{
difference_type d = 0;
return move(rows, d);
}
/// Current position, or -1 for unknown
/**
* The starting position, just before the first row, counts as position zero.
*
* Position may be unknown if (and only if) this cursor was adopted, and has
* never hit its starting position (position zero).
*/
difference_type pos() const noexcept { return m_pos; }
/// End position, or -1 for unknown
/**
* Returns the final position, just after the last row in the result set. The
* starting position, just before the first row, counts as position zero.
*
* End position is unknown until it is encountered during use.
*/
difference_type endpos() const noexcept { return m_endpos; }
/// Return zero-row result for this cursor.
result const &empty_result() const noexcept { return m_empty_result; }
void close() noexcept;
private:
difference_type adjust(difference_type hoped, difference_type actual);
static std::string stridestring(difference_type);
/// Initialize cached empty result. Call only at beginning or end!
void init_empty_result(transaction_base &);
/// Connection in which this cursor lives.
connection &m_home;
/// Zero-row result from this cursor (or plain empty one if cursor is
/// adopted)
result m_empty_result;
result m_cached_current_row;
/// Is this cursor adopted (as opposed to created by this cursor object)?
bool m_adopted;
/// Will this cursor object destroy its SQL cursor when it dies?
cursor_base::ownership_policy m_ownership;
/// At starting position (-1), somewhere in the middle (0), or past end (1)
int m_at_end;
/// Position, or -1 for unknown
difference_type m_pos;
/// End position, or -1 for unknown
difference_type m_endpos = -1;
};
PQXX_LIBEXPORT result_size_type obtain_stateless_cursor_size(sql_cursor &);
PQXX_LIBEXPORT result stateless_cursor_retrieve(
sql_cursor &, result::difference_type size,
result::difference_type begin_pos, result::difference_type end_pos);
} // namespace pqxx::internal
#endif

View File

@@ -0,0 +1,131 @@
/** Common implementation for statement parameter lists.
*
* These are used for both prepared statements and parameterized statements.
*
* DO NOT INCLUDE THIS FILE DIRECTLY. Other headers include it for you.
*
* Copyright (c) 2000-2022, Jeroen T. Vermeulen.
*
* See COPYING for copyright license. If you did not receive a file called
* COPYING with this source code, please notify the distributor of this
* mistake, or contact the author.
*/
#ifndef PQXX_H_STATEMENT_PARAMETER
#define PQXX_H_STATEMENT_PARAMETER
#include <cstring>
#include <functional>
#include <iterator>
#include <string>
#include <vector>
#include "pqxx/binarystring.hxx"
#include "pqxx/strconv.hxx"
#include "pqxx/util.hxx"
namespace pqxx::internal
{
template<typename ITERATOR>
constexpr inline auto const iterator_identity{
[](decltype(*std::declval<ITERATOR>()) x) { return x; }};
/// Marker type: pass a dynamically-determined number of statement parameters.
/** @deprecated Use @ref params instead.
*
* Normally when invoking a prepared or parameterised statement, the number
* of parameters is known at compile time. For instance,
* `t.exec_prepared("foo", 1, "x");` executes statement `foo` with two
* parameters, an `int` and a C string.
*
* But sometimes you may want to pass a number of parameters known only at run
* time. In those cases, a @ref dynamic_params encodes a dynamically
* determined number of parameters. You can mix these with regular, static
* parameter lists, and you can re-use them for multiple statement invocations.
*
* A dynamic_params object does not store copies of its parameters, so make
* sure they remain accessible until you've executed the statement.
*
* The ACCESSOR is an optional callable (such as a lambda). If you pass an
* accessor `a`, then each parameter `p` goes into your statement as `a(p)`.
*/
template<typename IT, typename ACCESSOR = decltype(iterator_identity<IT>)>
class dynamic_params
{
public:
/// Wrap a sequence of pointers or iterators.
constexpr dynamic_params(IT begin, IT end) :
m_begin(begin), m_end(end), m_accessor(iterator_identity<IT>)
{}
/// Wrap a sequence of pointers or iterators.
/** This version takes an accessor callable. If you pass an accessor `acc`,
* then any parameter `p` will go into the statement's parameter list as
* `acc(p)`.
*/
constexpr dynamic_params(IT begin, IT end, ACCESSOR &acc) :
m_begin(begin), m_end(end), m_accessor(acc)
{}
/// Wrap a container.
template<typename C>
explicit constexpr dynamic_params(C &container) :
dynamic_params(std::begin(container), std::end(container))
{}
/// Wrap a container.
/** This version takes an accessor callable. If you pass an accessor `acc`,
* then any parameter `p` will go into the statement's parameter list as
* `acc(p)`.
*/
template<typename C>
explicit constexpr dynamic_params(C &container, ACCESSOR &acc) :
dynamic_params(std::begin(container), std::end(container), acc)
{}
constexpr IT begin() const noexcept { return m_begin; }
constexpr IT end() const noexcept { return m_end; }
constexpr auto access(decltype(*std::declval<IT>()) value) const
-> decltype(std::declval<ACCESSOR>()(value))
{
return m_accessor(value);
}
private:
IT const m_begin, m_end;
ACCESSOR m_accessor = iterator_identity<IT>;
};
/// Internal type: encode statement parameters.
/** Compiles arguments for prepared statements and parameterised queries into
* a format that can be passed into libpq.
*
* Objects of this type are meant to be short-lived: a `c_params` lives and
* dies entirely within the call to execute. So, for example, if you pass in a
* non-null pointer as a parameter, @ref params may simply use that pointer as
* a parameter value, without arranging longer-term storage for the data to
* which it points. All values referenced by parameters must remain "live"
* until the parameterised or prepared statement has been executed.
*/
struct PQXX_LIBEXPORT c_params
{
c_params() = default;
/// Copying these objects is pointless and expensive. Don't do it.
c_params(c_params const &) = delete;
c_params(c_params &&) = default;
/// Pre-allocate storage for `n` parameters.
void reserve(std::size_t n) &;
/// As used by libpq: pointers to parameter values.
std::vector<char const *> values;
/// As used by libpq: lengths of non-null arguments, in bytes.
std::vector<int> lengths;
/// As used by libpq: effectively boolean "is this a binary parameter?"
std::vector<format> formats;
};
} // namespace pqxx::internal
#endif

View File

@@ -0,0 +1,105 @@
/** Stream iterators.
*
* Copyright (c) 2000-2022, Jeroen T. Vermeulen.
*
* See COPYING for copyright license. If you did not receive a file called
* COPYING with this source code, please notify the distributor of this
* mistake, or contact the author.
*/
#ifndef PQXX_H_STREAM_ITERATOR
#define PQXX_H_STREAM_ITERATOR
#include <memory>
namespace pqxx
{
class stream_from;
}
namespace pqxx::internal
{
// C++20: Replace with generator?
/// Input iterator for stream_from.
/** Just barely enough to support range-based "for" loops. Don't assume that
* any of the usual behaviour works beyond that.
*/
template<typename... TYPE> class stream_input_iterator
{
public:
using value_type = std::tuple<TYPE...>;
/// Construct an "end" iterator.
stream_input_iterator() = default;
explicit stream_input_iterator(stream_from &home) : m_home(&home)
{
advance();
}
stream_input_iterator(stream_input_iterator const &) = default;
stream_input_iterator &operator++()
{
advance();
return *this;
}
value_type const &operator*() const { return m_value; }
/// Comparison only works for comparing to end().
bool operator==(stream_input_iterator const &rhs) const
{
return m_home == rhs.m_home;
}
/// Comparison only works for comparing to end().
bool operator!=(stream_input_iterator const &rhs) const
{
return not(*this == rhs);
}
private:
void advance()
{
if (m_home == nullptr)
throw usage_error{"Moving stream_from iterator beyond end()."};
if (not((*m_home) >> m_value))
m_home = nullptr;
}
stream_from *m_home{nullptr};
value_type m_value;
};
// C++20: Replace with generator?
/// Iteration over a @ref stream_from.
template<typename... TYPE> class stream_input_iteration
{
public:
using iterator = stream_input_iterator<TYPE...>;
explicit stream_input_iteration(stream_from &home) : m_home{home} {}
iterator begin() const { return iterator{m_home}; }
iterator end() const { return {}; }
private:
stream_from &m_home;
};
// C++20: Replace with generator?
/// Iteration over a @ref stream_from, deleting it once done.
template<typename... TYPE> class owning_stream_input_iteration
{
public:
using iterator = stream_input_iterator<TYPE...>;
explicit owning_stream_input_iteration(std::unique_ptr<stream_from> &&home) :
m_home{std::move(home)}
{}
iterator begin() const { return iterator{*m_home.get()}; }
iterator end() const { return {}; }
private:
std::unique_ptr<stream_from> m_home;
};
} // namespace pqxx::internal
#endif

View File

@@ -0,0 +1,18 @@
#if !defined(PQXX_WAIT_HXX)
# define PQXX_WAIT_HXX
namespace pqxx::internal
{
/// Wait.
/** This is normally `std::this_thread::sleep_for()`. But MinGW's `thread`
* header doesn't work, so we must be careful about including it.
*/
void PQXX_LIBEXPORT wait_for(unsigned int microseconds);
/// Wait for a socket to be ready for reading/writing, or timeout.
PQXX_LIBEXPORT void wait_fd(
int fd, bool for_read, bool for_write, unsigned seconds = 1,
unsigned microseconds = 0);
} // namespace pqxx::internal
#endif