First Commit
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user