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>
75 Row(
Client& client,
const std::vector<Descriptor>& descriptors, std::span<const std::byte> message)
77 descriptors{&descriptors},
79 statusWrapper{client},
80 numericConverter{client},
81 calendarConverter{client}
96 return *
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE;
104 const auto&
descriptor = getDescriptor(index);
106 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
112 return message[
descriptor.offset] != std::byte{0};
115 throwInvalidType(
"bool",
descriptor.adjustedType);
122 std::optional<std::int16_t>
getInt16(
unsigned index)
124 std::optional<int> scale{0};
133 std::optional<int> scale;
135 return value.has_value() ? std::optional{
ScaledInt16{value.
value(), scale.value()}} : std::nullopt;
141 std::optional<std::int32_t>
getInt32(
unsigned index)
143 std::optional<int> scale{0};
152 std::optional<int> scale;
154 return value.has_value() ? std::optional{
ScaledInt32{value.
value(), scale.value()}} : std::nullopt;
160 std::optional<std::int64_t>
getInt64(
unsigned index)
162 std::optional<int> scale{0};
171 std::optional<int> scale;
173 return value.has_value() ? std::optional{
ScaledInt64{value.
value(), scale.value()}} : std::nullopt;
181 const auto&
descriptor = getDescriptor(index);
183 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
194 throwInvalidType(
"ScaledOpaqueInt128",
descriptor.adjustedType);
198#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
204 std::optional<int> scale{0};
206 return value.has_value() ? std::optional{value.value()} : std::nullopt;
214 std::optional<int> scale;
225 std::optional<int> scale{0};
234 std::optional<int> scale{0};
243 const auto&
descriptor = getDescriptor(index);
245 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
254 throwInvalidType(
"OpaqueDecFloat16",
descriptor.adjustedType);
258#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
264 std::optional<int> scale{0};
274 const auto&
descriptor = getDescriptor(index);
276 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
285 throwInvalidType(
"OpaqueDecFloat34",
descriptor.adjustedType);
289#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
295 std::optional<int> scale{0};
305 const auto&
descriptor = getDescriptor(index);
307 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
313 return calendarConverter.opaqueDateToDate(
317 throwInvalidType(
"Date",
descriptor.adjustedType);
326 const auto&
descriptor = getDescriptor(index);
328 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
337 throwInvalidType(
"OpaqueDate",
descriptor.adjustedType);
346 const auto&
descriptor = getDescriptor(index);
348 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
354 return calendarConverter.opaqueTimeToTime(
358 throwInvalidType(
"Time",
descriptor.adjustedType);
367 const auto&
descriptor = getDescriptor(index);
369 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
378 throwInvalidType(
"OpaqueTime",
descriptor.adjustedType);
387 const auto&
descriptor = getDescriptor(index);
389 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
395 return calendarConverter.opaqueTimestampToTimestamp(
399 throwInvalidType(
"Timestamp",
descriptor.adjustedType);
408 const auto&
descriptor = getDescriptor(index);
410 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
419 throwInvalidType(
"OpaqueTimestamp",
descriptor.adjustedType);
428 const auto&
descriptor = getDescriptor(index);
430 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
436 return calendarConverter.opaqueTimeTzToTimeTz(
440 throwInvalidType(
"TimeTz",
descriptor.adjustedType);
449 const auto&
descriptor = getDescriptor(index);
451 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
460 throwInvalidType(
"OpaqueTimeTz",
descriptor.adjustedType);
469 const auto&
descriptor = getDescriptor(index);
471 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
477 return calendarConverter.opaqueTimestampTzToTimestampTz(
481 throwInvalidType(
"TimestampTz",
descriptor.adjustedType);
490 const auto&
descriptor = getDescriptor(index);
492 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
501 throwInvalidType(
"OpaqueTimestampTz",
descriptor.adjustedType);
510 const auto&
descriptor = getDescriptor(index);
512 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
525 throwInvalidType(
"BlobId",
descriptor.adjustedType);
534 const auto&
descriptor = getDescriptor(index);
536 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
544 return (message[
descriptor.offset] != std::byte{0}) ? std::string{
"true"} : std::string{
"false"};
547 return numericConverter.numberToString(
551 return numericConverter.numberToString(
555 return numericConverter.numberToString(
559 return numericConverter.opaqueInt128ToString(
563 return numericConverter.numberToString(*
reinterpret_cast<const float*
>(
data));
566 return numericConverter.numberToString(*
reinterpret_cast<const double*
>(
data));
569 return calendarConverter.opaqueDateToString(*
reinterpret_cast<const OpaqueDate*
>(
data));
572 return calendarConverter.opaqueTimeToString(*
reinterpret_cast<const OpaqueTime*
>(
data));
575 return calendarConverter.opaqueTimestampToString(*
reinterpret_cast<const OpaqueTimestamp*
>(
data));
578 return calendarConverter.opaqueTimeTzToString(
582 return calendarConverter.opaqueTimestampTzToString(
586 return numericConverter.opaqueDecFloat16ToString(
590 return numericConverter.opaqueDecFloat34ToString(
594 return std::string{
reinterpret_cast<const char*
>(
data +
sizeof(std::uint16_t)),
595 *
reinterpret_cast<const std::uint16_t*
>(
data)};
598 throwInvalidType(
"std::string",
descriptor.adjustedType);
609 template <
typename T>
615 template <Aggregate T>
618 using namespace impl::reflection;
622 if (
N != descriptors->size())
625 ") does not match output column count (" + std::to_string(descriptors->size()) +
")");
634 template <TupleLike T>
637 using namespace impl::reflection;
639 constexpr std::size_t
N = std::tuple_size_v<T>;
641 if (
N != descriptors->size())
644 ") does not match output column count (" + std::to_string(descriptors->size()) +
")");
653 template <VariantLike V>
656 using namespace impl::reflection;
659 "Variant contains unsupported types. All variant alternatives must be types supported by fb-cpp "
660 "(e.g., std::int32_t, std::string, Date, ScaledOpaqueInt128, etc.). Check VariantTypeTraits.h for the "
661 "complete list of supported types.");
663 const auto&
descriptor = getDescriptor(index);
668 return V{std::monostate{}};
672 "NULL value encountered but variant does not contain std::monostate at index " +
673 std::to_string(index));
681 const Descriptor& getDescriptor(
unsigned index)
683 if (index >= descriptors->size())
684 throw std::out_of_range(
"index out of range");
686 return (*descriptors)[index];
689 template <
typename T, std::size_t...
Is>
690 T getStruct(std::index_sequence<Is...>)
692 using namespace impl::reflection;
697 template <
typename F>
698 auto getStructField(
unsigned index)
700 using namespace impl::reflection;
710 if (!
opt.has_value())
712 throw FbCppException(
713 "Null value encountered for non-optional field at index " + std::to_string(index));
716 return std::move(
opt.value());
720 template <
typename T, std::size_t...
Is>
721 T getTuple(std::index_sequence<Is...>)
723 using namespace impl::reflection;
728 template <
typename V>
729 V getVariantValue(
unsigned index,
const Descriptor&
descriptor)
731 using namespace impl::reflection;
749#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
765#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
779#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
788#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
812#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
880 template <
typename V, std::
size_t I = 0>
883 using namespace impl::reflection;
885 if constexpr (
I >= std::variant_size_v<V>)
887 throw FbCppException(
888 "Cannot convert SQL type to any variant alternative at index " + std::to_string(index));
892 using Alt = std::variant_alternative_t<I, V>;
894 if constexpr (std::is_same_v<Alt, std::monostate>)
901 return V{std::move(
opt.value())};
907 template <
typename T>
908 std::optional<T> getNumber(
unsigned index, std::optional<int>& scale,
const char*
typeName)
910 const auto&
descriptor = getDescriptor(index);
912 if (*
reinterpret_cast<const std::int16_t*
>(&message[
descriptor.nullOffset]) !=
FB_FALSE)
916#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
925#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
928 numericConverter.opaqueInt128ToBoostInt128(*
reinterpret_cast<const OpaqueInt128*
>(
data)));
933 boostDecFloat16.emplace(numericConverter.opaqueDecFloat16ToBoostDecFloat16(
939 boostDecFloat34.emplace(numericConverter.opaqueDecFloat34ToBoostDecFloat34(
954 throw FbCppException(
"Invalid type: actual type " + std::string(
actualType) +
", descriptor type " +
958 template <
typename T>
982 return numericConverter.numberToNumber<T>(
986 return numericConverter.numberToNumber<T>(
990 return numericConverter.numberToNumber<T>(
993#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
995 return numericConverter.numberToNumber<T>(
1000 return numericConverter.numberToNumber<T>(
1004 return numericConverter.numberToNumber<T>(
1009 return numericConverter.numberToNumber<T>(*
reinterpret_cast<const float*
>(
data),
toScale.value());
1012 return numericConverter.numberToNumber<T>(*
reinterpret_cast<const double*
>(
data),
toScale.value());
1021 const std::vector<Descriptor>* descriptors;
1022 std::span<const std::byte> message;
1023 impl::StatusWrapper statusWrapper;
1024 impl::NumericConverter numericConverter;
1025 impl::CalendarConverter calendarConverter;
1036 return getBool(index);
1042 return getBlobId(index);
1048 return getInt16(index);
1054 return getScaledInt16(index);
1060 return getInt32(index);
1066 return getScaledInt32(index);
1072 return getInt64(index);
1078 return getScaledInt64(index);
1084 return getScaledOpaqueInt128(index);
1087#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1091 return getBoostInt128(index);
1097 return getScaledBoostInt128(index);
1104 return getFloat(index);
1110 return getDouble(index);
1116 return getOpaqueDecFloat16(index);
1119#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1123 return getBoostDecFloat16(index);
1130 return getOpaqueDecFloat34(index);
1133#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1137 return getBoostDecFloat34(index);
1144 return getDate(index);
1150 return getOpaqueDate(index);
1156 return getTime(index);
1162 return getOpaqueTime(index);
1168 return getOpaqueTimestamp(index);
1174 return getTimestamp(index);
1180 return getTimeTz(index);
1186 return getOpaqueTimeTz(index);
1192 return getTimestampTz(index);
1198 return getOpaqueTimestampTz(index);
1204 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< 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.
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.
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.
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.
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.