32#include "NumericConverter.h"
33#include "CalendarConverter.h"
34#include "Descriptor.h"
36#include "StructBinding.h"
37#include "VariantTypeTraits.h"
47#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
48#include <boost/multiprecision/cpp_int.hpp>
49#include <boost/multiprecision/cpp_dec_float.hpp>
52#if FB_CPP_USE_BOOST_DECIMAL != 0
53#include <boost/decimal.hpp>
79 Row(
Client& client,
const std::vector<Descriptor>& descriptors, std::span<const std::byte> message)
81 descriptors{&descriptors},
83 statusWrapper{client},
84 numericConverter{client},
85 calendarConverter{client}
100 return *
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE;
108 const auto&
descriptor = getDescriptor(index);
110 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
116 return message[
descriptor.offset] != std::byte{0};
119 throwInvalidType(
"bool",
descriptor.adjustedType);
126 std::optional<std::int16_t>
getInt16(
unsigned index)
128 std::optional<int> scale{0};
137 std::optional<int> scale;
139 return value.has_value() ? std::optional{
ScaledInt16{value.
value(), scale.value()}} : std::nullopt;
145 std::optional<std::int32_t>
getInt32(
unsigned index)
147 std::optional<int> scale{0};
156 std::optional<int> scale;
158 return value.has_value() ? std::optional{
ScaledInt32{value.
value(), scale.value()}} : std::nullopt;
164 std::optional<std::int64_t>
getInt64(
unsigned index)
166 std::optional<int> scale{0};
175 std::optional<int> scale;
177 return value.has_value() ? std::optional{
ScaledInt64{value.
value(), scale.value()}} : std::nullopt;
185 const auto&
descriptor = getDescriptor(index);
187 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
198 throwInvalidType(
"ScaledOpaqueInt128",
descriptor.adjustedType);
202#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
208 std::optional<int> scale{0};
210 return value.has_value() ? std::optional{value.value()} : std::nullopt;
218 std::optional<int> scale;
229 std::optional<int> scale{0};
238 std::optional<int> scale{0};
247 const auto&
descriptor = getDescriptor(index);
249 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
258 throwInvalidType(
"OpaqueDecFloat16",
descriptor.adjustedType);
262#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
268 std::optional<int> scale{0};
273#if FB_CPP_USE_BOOST_DECIMAL != 0
279 std::optional<int> scale{0};
288 std::optional<int> scale{0};
298 const auto&
descriptor = getDescriptor(index);
300 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
309 throwInvalidType(
"OpaqueDecFloat34",
descriptor.adjustedType);
313#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
319 std::optional<int> scale{0};
324#if FB_CPP_USE_BOOST_DECIMAL != 0
330 std::optional<int> scale{0};
340 const auto&
descriptor = getDescriptor(index);
342 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
348 return calendarConverter.opaqueDateToDate(
352 throwInvalidType(
"Date",
descriptor.adjustedType);
361 const auto&
descriptor = getDescriptor(index);
363 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
372 throwInvalidType(
"OpaqueDate",
descriptor.adjustedType);
381 const auto&
descriptor = getDescriptor(index);
383 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
389 return calendarConverter.opaqueTimeToTime(
393 throwInvalidType(
"Time",
descriptor.adjustedType);
402 const auto&
descriptor = getDescriptor(index);
404 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
413 throwInvalidType(
"OpaqueTime",
descriptor.adjustedType);
422 const auto&
descriptor = getDescriptor(index);
424 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
430 return calendarConverter.opaqueTimestampToTimestamp(
434 throwInvalidType(
"Timestamp",
descriptor.adjustedType);
443 const auto&
descriptor = getDescriptor(index);
445 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
454 throwInvalidType(
"OpaqueTimestamp",
descriptor.adjustedType);
463 const auto&
descriptor = getDescriptor(index);
465 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
471 return calendarConverter.opaqueTimeTzToTimeTz(
475 throwInvalidType(
"TimeTz",
descriptor.adjustedType);
484 const auto&
descriptor = getDescriptor(index);
486 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
495 throwInvalidType(
"OpaqueTimeTz",
descriptor.adjustedType);
504 const auto&
descriptor = getDescriptor(index);
506 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
512 return calendarConverter.opaqueTimestampTzToTimestampTz(
516 throwInvalidType(
"TimestampTz",
descriptor.adjustedType);
525 const auto&
descriptor = getDescriptor(index);
527 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
536 throwInvalidType(
"OpaqueTimestampTz",
descriptor.adjustedType);
545 const auto&
descriptor = getDescriptor(index);
547 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
560 throwInvalidType(
"BlobId",
descriptor.adjustedType);
569 const auto&
descriptor = getDescriptor(index);
571 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
579 return (message[
descriptor.offset] != std::byte{0}) ? std::string{
"true"} : std::string{
"false"};
582 return numericConverter.numberToString(
586 return numericConverter.numberToString(
590 return numericConverter.numberToString(
594 return numericConverter.opaqueInt128ToString(
598 return numericConverter.numberToString(*
reinterpret_cast<const float*
>(
data));
601 return numericConverter.numberToString(*
reinterpret_cast<const double*
>(
data));
604 return calendarConverter.opaqueDateToString(*
reinterpret_cast<const OpaqueDate*
>(
data));
607 return calendarConverter.opaqueTimeToString(*
reinterpret_cast<const OpaqueTime*
>(
data));
610 return calendarConverter.opaqueTimestampToString(*
reinterpret_cast<const OpaqueTimestamp*
>(
data));
613 return calendarConverter.opaqueTimeTzToString(
617 return calendarConverter.opaqueTimestampTzToString(
621 return numericConverter.opaqueDecFloat16ToString(
625 return numericConverter.opaqueDecFloat34ToString(
629 return std::string{
reinterpret_cast<const char*
>(
data +
sizeof(std::uint16_t)),
630 *
reinterpret_cast<const std::uint16_t*
>(
data)};
633 throwInvalidType(
"std::string",
descriptor.adjustedType);
644 template <
typename T>
650 template <Aggregate T>
653 using namespace impl::reflection;
657 if (
N != descriptors->size())
660 ") does not match output column count (" + std::to_string(descriptors->size()) +
")");
669 template <TupleLike T>
672 using namespace impl::reflection;
674 constexpr std::size_t
N = std::tuple_size_v<T>;
676 if (
N != descriptors->size())
679 ") does not match output column count (" + std::to_string(descriptors->size()) +
")");
688 template <VariantLike V>
691 using namespace impl::reflection;
694 "Variant contains unsupported types. All variant alternatives must be types supported by fb-cpp "
695 "(e.g., std::int32_t, std::string, Date, ScaledOpaqueInt128, etc.). Check VariantTypeTraits.h for the "
696 "complete list of supported types.");
698 const auto&
descriptor = getDescriptor(index);
703 return V{std::monostate{}};
707 "NULL value encountered but variant does not contain std::monostate at index " +
708 std::to_string(index));
716 const Descriptor& getDescriptor(
unsigned index)
718 if (index >= descriptors->size())
719 throw std::out_of_range(
"index out of range");
721 return (*descriptors)[index];
724 template <
typename T, std::size_t...
Is>
725 T getStruct(std::index_sequence<Is...>)
727 using namespace impl::reflection;
732 template <
typename F>
733 auto getStructField(
unsigned index)
735 using namespace impl::reflection;
745 if (!
opt.has_value())
747 throw FbCppException(
748 "Null value encountered for non-optional field at index " + std::to_string(index));
751 return std::move(
opt.value());
755 template <
typename T, std::size_t...
Is>
756 T getTuple(std::index_sequence<Is...>)
758 using namespace impl::reflection;
763 template <
typename V>
764 V getVariantValue(
unsigned index,
const Descriptor&
descriptor)
766 using namespace impl::reflection;
784#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
800#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
814#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
826#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
850#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
854#if FB_CPP_USE_BOOST_DECIMAL != 0
863#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
867#if FB_CPP_USE_BOOST_DECIMAL != 0
925 template <
typename V, std::
size_t I = 0>
928 using namespace impl::reflection;
930 if constexpr (
I >= std::variant_size_v<V>)
932 throw FbCppException(
933 "Cannot convert SQL type to any variant alternative at index " + std::to_string(index));
937 using Alt = std::variant_alternative_t<I, V>;
939 if constexpr (std::is_same_v<Alt, std::monostate>)
946 return V{std::move(
opt.value())};
952 template <
typename T>
953 std::optional<T> getNumber(
unsigned index, std::optional<int>& scale,
const char*
typeName)
955 const auto&
descriptor = getDescriptor(index);
957 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
961#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
964#if FB_CPP_USE_BOOST_DECIMAL != 0
967#elif FB_CPP_USE_BOOST_MULTIPRECISION != 0
975#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
977 boostInt128.emplace(numericConverter.opaqueInt128ToBoostInt128(
983#if FB_CPP_USE_BOOST_DECIMAL != 0
985 boostDecimal64.emplace(numericConverter.opaqueDecFloat16ToBoostDecimal64(
991 boostDecimal128.emplace(numericConverter.opaqueDecFloat34ToBoostDecimal128(
995#elif FB_CPP_USE_BOOST_MULTIPRECISION != 0
997 boostDecFloat16.emplace(numericConverter.opaqueDecFloat16ToBoostDecFloat16(
1003 boostDecFloat34.emplace(numericConverter.opaqueDecFloat34ToBoostDecFloat34(
1018 throw FbCppException(
"Invalid type: actual type " + std::string(
actualType) +
", descriptor type " +
1022 template <
typename T>
1046 return numericConverter.numberToNumber<T>(
1050 return numericConverter.numberToNumber<T>(
1054 return numericConverter.numberToNumber<T>(
1057#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1059 return numericConverter.numberToNumber<T>(
1064#if FB_CPP_USE_BOOST_DECIMAL != 0
1066 return numericConverter.numberToNumber<T>(
1070 return numericConverter.numberToNumber<T>(
1072#elif FB_CPP_USE_BOOST_MULTIPRECISION != 0
1074 return numericConverter.numberToNumber<T>(
1078 return numericConverter.numberToNumber<T>(
1083 return numericConverter.numberToNumber<T>(*
reinterpret_cast<const float*
>(
data),
toScale.value());
1086 return numericConverter.numberToNumber<T>(*
reinterpret_cast<const double*
>(
data),
toScale.value());
1095 const std::vector<Descriptor>* descriptors;
1096 std::span<const std::byte> message;
1097 impl::StatusWrapper statusWrapper;
1098 impl::NumericConverter numericConverter;
1099 impl::CalendarConverter calendarConverter;
1110 return getBool(index);
1116 return getBlobId(index);
1122 return getInt16(index);
1128 return getScaledInt16(index);
1134 return getInt32(index);
1140 return getScaledInt32(index);
1146 return getInt64(index);
1152 return getScaledInt64(index);
1158 return getScaledOpaqueInt128(index);
1161#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1165 return getBoostInt128(index);
1171 return getScaledBoostInt128(index);
1178 return getFloat(index);
1184 return getDouble(index);
1190 return getOpaqueDecFloat16(index);
1193#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1197 return getBoostDecFloat16(index);
1201#if FB_CPP_USE_BOOST_DECIMAL != 0
1205 return getBoostDecimal32(index);
1211 return getBoostDecimal64(index);
1218 return getOpaqueDecFloat34(index);
1221#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1225 return getBoostDecFloat34(index);
1229#if FB_CPP_USE_BOOST_DECIMAL != 0
1233 return getBoostDecimal128(index);
1240 return getDate(index);
1246 return getOpaqueDate(index);
1252 return getTime(index);
1258 return getOpaqueTime(index);
1264 return getOpaqueTimestamp(index);
1270 return getTimestamp(index);
1276 return getTimeTz(index);
1282 return getOpaqueTimeTz(index);
1288 return getTimestampTz(index);
1294 return getOpaqueTimestampTz(index);
1300 return getString(index);
Represents a Firebird blob identifier.
ISC_QUAD id
Stores the raw Firebird blob identifier value.
Represents a Firebird client library instance.
Base exception class for all fb-cpp exceptions.
A lightweight, non-owning view of a single row's data with typed accessors.
std::optional< BoostDecFloat16 > getBoostDecFloat16(unsigned index)
Reads a Boost-based 16-digit decimal floating-point column.
std::optional< std::int32_t > getInt32(unsigned index)
Reads a 32-bit signed integer column.
std::optional< OpaqueTimestamp > getOpaqueTimestamp(unsigned index)
Reads a raw timestamp column in Firebird's representation.
T get()
Retrieves all output columns into a user-defined aggregate struct.
std::optional< Timestamp > getTimestamp(unsigned index)
Reads a timestamp column without timezone.
std::optional< Time > getTime(unsigned index)
Reads a time-of-day column without timezone.
std::optional< TimestampTz > getTimestampTz(unsigned index)
Reads a timestamp-with-time-zone column.
std::optional< ScaledInt16 > getScaledInt16(unsigned index)
Reads a scaled 16-bit signed integer column.
std::optional< float > getFloat(unsigned index)
Reads a single precision floating-point column.
Row(Client &client, const std::vector< Descriptor > &descriptors, std::span< const std::byte > message)
Constructs a Row view over the given message buffer.
std::optional< OpaqueTimestampTz > getOpaqueTimestampTz(unsigned index)
Reads a raw timestamp-with-time-zone column in Firebird's representation.
std::optional< BoostInt128 > getBoostInt128(unsigned index)
Reads a Boost 128-bit integer column.
std::optional< TimeTz > getTimeTz(unsigned index)
Reads a time-of-day column with timezone.
V get(unsigned index)
Retrieves a column value as a user-defined variant type.
std::optional< OpaqueTimeTz > getOpaqueTimeTz(unsigned index)
Reads a raw time-of-day column with timezone in Firebird's representation.
std::optional< ScaledOpaqueInt128 > getScaledOpaqueInt128(unsigned index)
Reads a Firebird scaled 128-bit integer column.
std::optional< std::int64_t > getInt64(unsigned index)
Reads a 64-bit signed integer column.
std::optional< ScaledBoostInt128 > getScaledBoostInt128(unsigned index)
Reads a scaled Boost 128-bit integer column.
std::optional< ScaledInt32 > getScaledInt32(unsigned index)
Reads a scaled 32-bit signed integer column.
std::optional< BoostDecimal64 > getBoostDecimal64(unsigned index)
Reads a Boost.Decimal 16-digit decimal floating-point column.
std::optional< OpaqueDecFloat16 > getOpaqueDecFloat16(unsigned index)
Reads a Firebird 16-digit decimal floating-point column.
std::optional< double > getDouble(unsigned index)
Reads a double precision floating-point column.
std::optional< BoostDecimal32 > getBoostDecimal32(unsigned index)
Reads a Boost.Decimal 7-digit decimal floating-point column.
bool isNull(unsigned index)
Reports whether the row has a null at the given column.
std::optional< BlobId > getBlobId(unsigned index)
Reads a blob identifier column.
std::optional< std::int16_t > getInt16(unsigned index)
Reads a 16-bit signed integer column.
std::optional< OpaqueDate > getOpaqueDate(unsigned index)
Reads a raw date column in Firebird's representation.
std::optional< OpaqueDecFloat34 > getOpaqueDecFloat34(unsigned index)
Reads a Firebird 34-digit decimal floating-point column.
std::optional< Date > getDate(unsigned index)
Reads a date column.
std::optional< ScaledInt64 > getScaledInt64(unsigned index)
Reads a scaled 64-bit signed integer column.
std::optional< BoostDecFloat34 > getBoostDecFloat34(unsigned index)
Reads a Boost-based 34-digit decimal floating-point column.
std::optional< std::string > getString(unsigned index)
Reads a textual column, applying number-to-string conversions when needed.
std::optional< bool > getBool(unsigned index)
Reads a boolean column.
std::optional< OpaqueTime > getOpaqueTime(unsigned index)
Reads a raw time-of-day column in Firebird's representation.
std::optional< BoostDecimal128 > getBoostDecimal128(unsigned index)
Reads a Boost.Decimal 34-digit decimal floating-point column.
T get(unsigned index)
Retrieves a column using the most appropriate typed accessor specialization.
ScaledNumber< std::int64_t > ScaledInt64
Signed 64-bit scaled number.
ScaledNumber< std::int32_t > ScaledInt32
Signed 32-bit scaled number.
boost::multiprecision::number< boost::multiprecision::cpp_dec_float< 34 > > BoostDecFloat34
34-digit decimal floating point using Boost.Multiprecision.
FB_DEC16 OpaqueDecFloat16
Opaque 16-digit decimal floating point exposed by the Firebird API.
ScaledNumber< std::int16_t > ScaledInt16
Signed 16-bit scaled number.
boost::multiprecision::number< boost::multiprecision::cpp_dec_float< 16 > > BoostDecFloat16
16-digit decimal floating point using Boost.Multiprecision.
boost::decimal::decimal128_t BoostDecimal128
34-digit decimal floating point using Boost.Decimal.
DescriptorAdjustedType
Descriptor adjusted type.
@ BLOB
Binary large object.
@ TIME
Time of day without time zone.
@ DECFLOAT34
34-digit decimal floating point.
@ INT64
64-bit signed integer.
@ TIME_TZ
Time of day with time zone.
@ DECFLOAT16
16-digit decimal floating point.
@ INT16
16-bit signed integer.
@ STRING
String type (variable-length).
@ INT32
32-bit signed integer.
@ TIMESTAMP
Timestamp without time zone.
@ TIMESTAMP_TZ
Timestamp with time zone.
@ INT128
128-bit signed integer.
@ FLOAT
Single-precision floating point.
@ DOUBLE
Double-precision floating point.
FB_I128 OpaqueInt128
Opaque 128-bit integer exposed by the Firebird API.
FB_DEC34 OpaqueDecFloat34
Opaque 34-digit decimal floating point exposed by the Firebird API.
boost::multiprecision::int128_t BoostInt128
128-bit integer using Boost.Multiprecision.
ScaledNumber< BoostInt128 > ScaledBoostInt128
Scaled 128-bit integer backed by Boost.Multiprecision.
boost::decimal::decimal64_t BoostDecimal64
16-digit decimal floating point using Boost.Decimal.
Describes a parameter or column.
Wrapper for Firebird date values.
Wrapper for Firebird time-with-time-zone values.
Wrapper for Firebird time values.
Wrapper for Firebird timestamp-with-time-zone values.
Wrapper for Firebird timestamp values.
Represents a numeric value with an explicit decimal scale.
int scale
Decimal scale applied to value.
T value
Unscaled numeric value.