fb-cpp 0.0.2
A modern C++ wrapper for the Firebird database API
Loading...
Searching...
No Matches
Row.h
1/*
2 * MIT License
3 *
4 * Copyright (c) 2026 F.D.Castel
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25#ifndef FBCPP_ROW_H
26#define FBCPP_ROW_H
27
28#include "config.h"
29#include "fb-api.h"
30#include "types.h"
31#include "Blob.h"
32#include "NumericConverter.h"
33#include "CalendarConverter.h"
34#include "Descriptor.h"
35#include "Exception.h"
36#include "StructBinding.h"
37#include "VariantTypeTraits.h"
38#include <cassert>
39#include <cstddef>
40#include <cstdint>
41#include <optional>
42#include <span>
43#include <stdexcept>
44#include <string>
45#include <vector>
46
47#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
48#include <boost/multiprecision/cpp_int.hpp>
49#include <boost/multiprecision/cpp_dec_float.hpp>
50#endif
51
52
56namespace fbcpp
57{
65 class Row final
66 {
67 public:
75 Row(Client& client, const std::vector<Descriptor>& descriptors, std::span<const std::byte> message)
76 : client{&client},
77 descriptors{&descriptors},
78 message{message},
79 statusWrapper{client},
80 numericConverter{client},
81 calendarConverter{client}
82 {
83 }
84
85 public:
89
93 bool isNull(unsigned index)
94 {
95 const auto& descriptor = getDescriptor(index);
96 return *reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE;
97 }
98
102 std::optional<bool> getBool(unsigned index)
103 {
104 const auto& descriptor = getDescriptor(index);
105
106 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
107 return std::nullopt;
108
109 switch (descriptor.adjustedType)
110 {
112 return message[descriptor.offset] != std::byte{0};
113
114 default:
115 throwInvalidType("bool", descriptor.adjustedType);
116 }
117 }
118
122 std::optional<std::int16_t> getInt16(unsigned index)
123 {
124 std::optional<int> scale{0};
125 return getNumber<std::int16_t>(index, scale, "std::int16_t");
126 }
127
131 std::optional<ScaledInt16> getScaledInt16(unsigned index)
132 {
133 std::optional<int> scale;
134 const auto value = getNumber<std::int16_t>(index, scale, "ScaledInt16");
135 return value.has_value() ? std::optional{ScaledInt16{value.value(), scale.value()}} : std::nullopt;
136 }
137
141 std::optional<std::int32_t> getInt32(unsigned index)
142 {
143 std::optional<int> scale{0};
144 return getNumber<std::int32_t>(index, scale, "std::int32_t");
145 }
146
150 std::optional<ScaledInt32> getScaledInt32(unsigned index)
151 {
152 std::optional<int> scale;
153 const auto value = getNumber<std::int32_t>(index, scale, "ScaledInt32");
154 return value.has_value() ? std::optional{ScaledInt32{value.value(), scale.value()}} : std::nullopt;
155 }
156
160 std::optional<std::int64_t> getInt64(unsigned index)
161 {
162 std::optional<int> scale{0};
163 return getNumber<std::int64_t>(index, scale, "std::int64_t");
164 }
165
169 std::optional<ScaledInt64> getScaledInt64(unsigned index)
170 {
171 std::optional<int> scale;
172 const auto value = getNumber<std::int64_t>(index, scale, "ScaledInt64");
173 return value.has_value() ? std::optional{ScaledInt64{value.value(), scale.value()}} : std::nullopt;
174 }
175
179 std::optional<ScaledOpaqueInt128> getScaledOpaqueInt128(unsigned index)
180 {
181 const auto& descriptor = getDescriptor(index);
182
183 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
184 return std::nullopt;
185
186 switch (descriptor.adjustedType)
187 {
189 return ScaledOpaqueInt128{
190 OpaqueInt128{*reinterpret_cast<const OpaqueInt128*>(&message[descriptor.offset])},
191 descriptor.scale};
192
193 default:
194 throwInvalidType("ScaledOpaqueInt128", descriptor.adjustedType);
195 }
196 }
197
198#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
202 std::optional<BoostInt128> getBoostInt128(unsigned index)
203 {
204 std::optional<int> scale{0};
205 const auto value = getNumber<BoostInt128>(index, scale, "BoostInt128");
206 return value.has_value() ? std::optional{value.value()} : std::nullopt;
207 }
208
212 std::optional<ScaledBoostInt128> getScaledBoostInt128(unsigned index)
213 {
214 std::optional<int> scale;
215 const auto value = getNumber<BoostInt128>(index, scale, "ScaledBoostInt128");
216 return value.has_value() ? std::optional{ScaledBoostInt128{value.value(), scale.value()}} : std::nullopt;
217 }
218#endif
219
223 std::optional<float> getFloat(unsigned index)
224 {
225 std::optional<int> scale{0};
226 return getNumber<float>(index, scale, "float");
227 }
228
232 std::optional<double> getDouble(unsigned index)
233 {
234 std::optional<int> scale{0};
235 return getNumber<double>(index, scale, "double");
236 }
237
241 std::optional<OpaqueDecFloat16> getOpaqueDecFloat16(unsigned index)
242 {
243 const auto& descriptor = getDescriptor(index);
244
245 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
246 return std::nullopt;
247
248 switch (descriptor.adjustedType)
249 {
251 return OpaqueDecFloat16{*reinterpret_cast<const OpaqueDecFloat16*>(&message[descriptor.offset])};
252
253 default:
254 throwInvalidType("OpaqueDecFloat16", descriptor.adjustedType);
255 }
256 }
257
258#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
262 std::optional<BoostDecFloat16> getBoostDecFloat16(unsigned index)
263 {
264 std::optional<int> scale{0};
265 return getNumber<BoostDecFloat16>(index, scale, "BoostDecFloat16");
266 }
267#endif
268
272 std::optional<OpaqueDecFloat34> getOpaqueDecFloat34(unsigned index)
273 {
274 const auto& descriptor = getDescriptor(index);
275
276 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
277 return std::nullopt;
278
279 switch (descriptor.adjustedType)
280 {
282 return OpaqueDecFloat34{*reinterpret_cast<const OpaqueDecFloat34*>(&message[descriptor.offset])};
283
284 default:
285 throwInvalidType("OpaqueDecFloat34", descriptor.adjustedType);
286 }
287 }
288
289#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
293 std::optional<BoostDecFloat34> getBoostDecFloat34(unsigned index)
294 {
295 std::optional<int> scale{0};
296 return getNumber<BoostDecFloat34>(index, scale, "BoostDecFloat34");
297 }
298#endif
299
303 std::optional<Date> getDate(unsigned index)
304 {
305 const auto& descriptor = getDescriptor(index);
306
307 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
308 return std::nullopt;
309
310 switch (descriptor.adjustedType)
311 {
313 return calendarConverter.opaqueDateToDate(
314 *reinterpret_cast<const OpaqueDate*>(&message[descriptor.offset]));
315
316 default:
317 throwInvalidType("Date", descriptor.adjustedType);
318 }
319 }
320
324 std::optional<OpaqueDate> getOpaqueDate(unsigned index)
325 {
326 const auto& descriptor = getDescriptor(index);
327
328 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
329 return std::nullopt;
330
331 switch (descriptor.adjustedType)
332 {
334 return OpaqueDate{*reinterpret_cast<const OpaqueDate*>(&message[descriptor.offset])};
335
336 default:
337 throwInvalidType("OpaqueDate", descriptor.adjustedType);
338 }
339 }
340
344 std::optional<Time> getTime(unsigned index)
345 {
346 const auto& descriptor = getDescriptor(index);
347
348 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
349 return std::nullopt;
350
351 switch (descriptor.adjustedType)
352 {
354 return calendarConverter.opaqueTimeToTime(
355 *reinterpret_cast<const OpaqueTime*>(&message[descriptor.offset]));
356
357 default:
358 throwInvalidType("Time", descriptor.adjustedType);
359 }
360 }
361
365 std::optional<OpaqueTime> getOpaqueTime(unsigned index)
366 {
367 const auto& descriptor = getDescriptor(index);
368
369 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
370 return std::nullopt;
371
372 switch (descriptor.adjustedType)
373 {
375 return OpaqueTime{*reinterpret_cast<const OpaqueTime*>(&message[descriptor.offset])};
376
377 default:
378 throwInvalidType("OpaqueTime", descriptor.adjustedType);
379 }
380 }
381
385 std::optional<Timestamp> getTimestamp(unsigned index)
386 {
387 const auto& descriptor = getDescriptor(index);
388
389 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
390 return std::nullopt;
391
392 switch (descriptor.adjustedType)
393 {
395 return calendarConverter.opaqueTimestampToTimestamp(
396 *reinterpret_cast<const OpaqueTimestamp*>(&message[descriptor.offset]));
397
398 default:
399 throwInvalidType("Timestamp", descriptor.adjustedType);
400 }
401 }
402
406 std::optional<OpaqueTimestamp> getOpaqueTimestamp(unsigned index)
407 {
408 const auto& descriptor = getDescriptor(index);
409
410 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
411 return std::nullopt;
412
413 switch (descriptor.adjustedType)
414 {
416 return OpaqueTimestamp{*reinterpret_cast<const OpaqueTimestamp*>(&message[descriptor.offset])};
417
418 default:
419 throwInvalidType("OpaqueTimestamp", descriptor.adjustedType);
420 }
421 }
422
426 std::optional<TimeTz> getTimeTz(unsigned index)
427 {
428 const auto& descriptor = getDescriptor(index);
429
430 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
431 return std::nullopt;
432
433 switch (descriptor.adjustedType)
434 {
436 return calendarConverter.opaqueTimeTzToTimeTz(
437 &statusWrapper, *reinterpret_cast<const OpaqueTimeTz*>(&message[descriptor.offset]));
438
439 default:
440 throwInvalidType("TimeTz", descriptor.adjustedType);
441 }
442 }
443
447 std::optional<OpaqueTimeTz> getOpaqueTimeTz(unsigned index)
448 {
449 const auto& descriptor = getDescriptor(index);
450
451 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
452 return std::nullopt;
453
454 switch (descriptor.adjustedType)
455 {
457 return OpaqueTimeTz{*reinterpret_cast<const OpaqueTimeTz*>(&message[descriptor.offset])};
458
459 default:
460 throwInvalidType("OpaqueTimeTz", descriptor.adjustedType);
461 }
462 }
463
467 std::optional<TimestampTz> getTimestampTz(unsigned index)
468 {
469 const auto& descriptor = getDescriptor(index);
470
471 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
472 return std::nullopt;
473
474 switch (descriptor.adjustedType)
475 {
477 return calendarConverter.opaqueTimestampTzToTimestampTz(
478 &statusWrapper, *reinterpret_cast<const OpaqueTimestampTz*>(&message[descriptor.offset]));
479
480 default:
481 throwInvalidType("TimestampTz", descriptor.adjustedType);
482 }
483 }
484
488 std::optional<OpaqueTimestampTz> getOpaqueTimestampTz(unsigned index)
489 {
490 const auto& descriptor = getDescriptor(index);
491
492 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
493 return std::nullopt;
494
495 switch (descriptor.adjustedType)
496 {
498 return OpaqueTimestampTz{*reinterpret_cast<const OpaqueTimestampTz*>(&message[descriptor.offset])};
499
500 default:
501 throwInvalidType("OpaqueTimestampTz", descriptor.adjustedType);
502 }
503 }
504
508 std::optional<BlobId> getBlobId(unsigned index)
509 {
510 const auto& descriptor = getDescriptor(index);
511
512 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
513 return std::nullopt;
514
515 switch (descriptor.adjustedType)
516 {
518 {
519 BlobId value;
520 value.id = *reinterpret_cast<const ISC_QUAD*>(&message[descriptor.offset]);
521 return value;
522 }
523
524 default:
525 throwInvalidType("BlobId", descriptor.adjustedType);
526 }
527 }
528
532 std::optional<std::string> getString(unsigned index)
533 {
534 const auto& descriptor = getDescriptor(index);
535
536 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
537 return std::nullopt;
538
539 const auto data = &message[descriptor.offset];
540
541 switch (descriptor.adjustedType)
542 {
544 return (message[descriptor.offset] != std::byte{0}) ? std::string{"true"} : std::string{"false"};
545
547 return numericConverter.numberToString(
548 ScaledInt16{*reinterpret_cast<const std::int16_t*>(data), descriptor.scale});
549
551 return numericConverter.numberToString(
552 ScaledInt32{*reinterpret_cast<const std::int32_t*>(data), descriptor.scale});
553
555 return numericConverter.numberToString(
556 ScaledInt64{*reinterpret_cast<const std::int64_t*>(data), descriptor.scale});
557
559 return numericConverter.opaqueInt128ToString(
560 &statusWrapper, *reinterpret_cast<const OpaqueInt128*>(data), descriptor.scale);
561
563 return numericConverter.numberToString(*reinterpret_cast<const float*>(data));
564
566 return numericConverter.numberToString(*reinterpret_cast<const double*>(data));
567
569 return calendarConverter.opaqueDateToString(*reinterpret_cast<const OpaqueDate*>(data));
570
572 return calendarConverter.opaqueTimeToString(*reinterpret_cast<const OpaqueTime*>(data));
573
575 return calendarConverter.opaqueTimestampToString(*reinterpret_cast<const OpaqueTimestamp*>(data));
576
578 return calendarConverter.opaqueTimeTzToString(
579 &statusWrapper, *reinterpret_cast<const OpaqueTimeTz*>(data));
580
582 return calendarConverter.opaqueTimestampTzToString(
583 &statusWrapper, *reinterpret_cast<const OpaqueTimestampTz*>(data));
584
586 return numericConverter.opaqueDecFloat16ToString(
587 &statusWrapper, *reinterpret_cast<const OpaqueDecFloat16*>(data));
588
590 return numericConverter.opaqueDecFloat34ToString(
591 &statusWrapper, *reinterpret_cast<const OpaqueDecFloat34*>(data));
592
594 return std::string{reinterpret_cast<const char*>(data + sizeof(std::uint16_t)),
595 *reinterpret_cast<const std::uint16_t*>(data)};
596
597 default:
598 throwInvalidType("std::string", descriptor.adjustedType);
599 }
600 }
601
605
609 template <typename T>
610 T get(unsigned index);
611
615 template <Aggregate T>
616 T get()
617 {
618 using namespace impl::reflection;
619
620 constexpr std::size_t N = fieldCountV<T>;
621
622 if (N != descriptors->size())
623 {
624 throw FbCppException("Struct field count (" + std::to_string(N) +
625 ") does not match output column count (" + std::to_string(descriptors->size()) + ")");
626 }
627
628 return getStruct<T>(std::make_index_sequence<N>{});
629 }
630
634 template <TupleLike T>
635 T get()
636 {
637 using namespace impl::reflection;
638
639 constexpr std::size_t N = std::tuple_size_v<T>;
640
641 if (N != descriptors->size())
642 {
643 throw FbCppException("Tuple element count (" + std::to_string(N) +
644 ") does not match output column count (" + std::to_string(descriptors->size()) + ")");
645 }
646
647 return getTuple<T>(std::make_index_sequence<N>{});
648 }
649
653 template <VariantLike V>
654 V get(unsigned index)
655 {
656 using namespace impl::reflection;
657
658 static_assert(variantAlternativesSupportedV<V>,
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.");
662
663 const auto& descriptor = getDescriptor(index);
664
665 if (isNull(index))
666 {
667 if constexpr (variantContainsV<std::monostate, V>)
668 return V{std::monostate{}};
669 else
670 {
671 throw FbCppException(
672 "NULL value encountered but variant does not contain std::monostate at index " +
673 std::to_string(index));
674 }
675 }
676
677 return getVariantValue<V>(index, descriptor);
678 }
679
680 private:
681 const Descriptor& getDescriptor(unsigned index)
682 {
683 if (index >= descriptors->size())
684 throw std::out_of_range("index out of range");
685
686 return (*descriptors)[index];
687 }
688
689 template <typename T, std::size_t... Is>
690 T getStruct(std::index_sequence<Is...>)
691 {
692 using namespace impl::reflection;
693
694 return T{getStructField<FieldType<T, Is>>(static_cast<unsigned>(Is))...};
695 }
696
697 template <typename F>
698 auto getStructField(unsigned index)
699 {
700 using namespace impl::reflection;
701
702 if constexpr (isOptionalV<F>)
703 return get<F>(index);
704 else if constexpr (isVariantV<F>)
705 return get<F>(index);
706 else
707 {
708 auto opt = get<std::optional<F>>(index);
709
710 if (!opt.has_value())
711 {
712 throw FbCppException(
713 "Null value encountered for non-optional field at index " + std::to_string(index));
714 }
715
716 return std::move(opt.value());
717 }
718 }
719
720 template <typename T, std::size_t... Is>
721 T getTuple(std::index_sequence<Is...>)
722 {
723 using namespace impl::reflection;
724
725 return T{getStructField<std::tuple_element_t<Is, T>>(static_cast<unsigned>(Is))...};
726 }
727
728 template <typename V>
729 V getVariantValue(unsigned index, const Descriptor& descriptor)
730 {
731 using namespace impl::reflection;
732
733 switch (descriptor.adjustedType)
734 {
736 if constexpr (variantContainsV<bool, V>)
737 return V{get<std::optional<bool>>(index).value()};
738 break;
739
741 if (descriptor.scale != 0)
742 {
743 if constexpr (variantContainsV<ScaledInt16, V>)
744 return V{get<std::optional<ScaledInt16>>(index).value()};
745 if constexpr (variantContainsV<ScaledInt32, V>)
746 return V{get<std::optional<ScaledInt32>>(index).value()};
747 if constexpr (variantContainsV<ScaledInt64, V>)
748 return V{get<std::optional<ScaledInt64>>(index).value()};
749#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
750 if constexpr (variantContainsV<ScaledBoostInt128, V>)
751 return V{get<std::optional<ScaledBoostInt128>>(index).value()};
752#endif
753 }
754 if constexpr (variantContainsV<std::int16_t, V>)
755 return V{get<std::optional<std::int16_t>>(index).value()};
756 break;
757
759 if (descriptor.scale != 0)
760 {
761 if constexpr (variantContainsV<ScaledInt32, V>)
762 return V{get<std::optional<ScaledInt32>>(index).value()};
763 if constexpr (variantContainsV<ScaledInt64, V>)
764 return V{get<std::optional<ScaledInt64>>(index).value()};
765#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
766 if constexpr (variantContainsV<ScaledBoostInt128, V>)
767 return V{get<std::optional<ScaledBoostInt128>>(index).value()};
768#endif
769 }
770 if constexpr (variantContainsV<std::int32_t, V>)
771 return V{get<std::optional<std::int32_t>>(index).value()};
772 break;
773
775 if (descriptor.scale != 0)
776 {
777 if constexpr (variantContainsV<ScaledInt64, V>)
778 return V{get<std::optional<ScaledInt64>>(index).value()};
779#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
780 if constexpr (variantContainsV<ScaledBoostInt128, V>)
781 return V{get<std::optional<ScaledBoostInt128>>(index).value()};
782#endif
783 }
784 if constexpr (variantContainsV<std::int64_t, V>)
785 return V{get<std::optional<std::int64_t>>(index).value()};
786 break;
787
788#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
790 if constexpr (variantContainsV<ScaledOpaqueInt128, V>)
791 return V{get<std::optional<ScaledOpaqueInt128>>(index).value()};
792 else if (descriptor.scale != 0)
793 {
794 if constexpr (variantContainsV<ScaledBoostInt128, V>)
795 return V{get<std::optional<ScaledBoostInt128>>(index).value()};
796 }
797 else if constexpr (variantContainsV<BoostInt128, V>)
798 return V{get<std::optional<BoostInt128>>(index).value()};
799 break;
800#endif
801
803 if constexpr (variantContainsV<float, V>)
804 return V{get<std::optional<float>>(index).value()};
805 break;
806
808 if constexpr (variantContainsV<double, V>)
809 return V{get<std::optional<double>>(index).value()};
810 break;
811
812#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
814 if constexpr (variantContainsV<OpaqueDecFloat16, V>)
815 return V{get<std::optional<OpaqueDecFloat16>>(index).value()};
816 else if constexpr (variantContainsV<BoostDecFloat16, V>)
817 return V{get<std::optional<BoostDecFloat16>>(index).value()};
818 break;
819
821 if constexpr (variantContainsV<OpaqueDecFloat34, V>)
822 return V{get<std::optional<OpaqueDecFloat34>>(index).value()};
823 else if constexpr (variantContainsV<BoostDecFloat34, V>)
824 return V{get<std::optional<BoostDecFloat34>>(index).value()};
825 break;
826#endif
827
829 if constexpr (variantContainsV<std::string, V>)
830 return V{get<std::optional<std::string>>(index).value()};
831 break;
832
834 if constexpr (variantContainsV<OpaqueDate, V>)
835 return V{get<std::optional<OpaqueDate>>(index).value()};
836 else if constexpr (variantContainsV<Date, V>)
837 return V{get<std::optional<Date>>(index).value()};
838 break;
839
841 if constexpr (variantContainsV<OpaqueTime, V>)
842 return V{get<std::optional<OpaqueTime>>(index).value()};
843 else if constexpr (variantContainsV<Time, V>)
844 return V{get<std::optional<Time>>(index).value()};
845 break;
846
848 if constexpr (variantContainsV<OpaqueTimestamp, V>)
849 return V{get<std::optional<OpaqueTimestamp>>(index).value()};
850 else if constexpr (variantContainsV<Timestamp, V>)
851 return V{get<std::optional<Timestamp>>(index).value()};
852 break;
853
855 if constexpr (variantContainsV<OpaqueTimeTz, V>)
856 return V{get<std::optional<OpaqueTimeTz>>(index).value()};
857 else if constexpr (variantContainsV<TimeTz, V>)
858 return V{get<std::optional<TimeTz>>(index).value()};
859 break;
860
862 if constexpr (variantContainsV<OpaqueTimestampTz, V>)
863 return V{get<std::optional<OpaqueTimestampTz>>(index).value()};
864 else if constexpr (variantContainsV<TimestampTz, V>)
865 return V{get<std::optional<TimestampTz>>(index).value()};
866 break;
867
869 if constexpr (variantContainsV<BlobId, V>)
870 return V{get<std::optional<BlobId>>(index).value()};
871 break;
872
873 default:
874 break;
875 }
876
877 return tryVariantAlternatives<V, 0>(index, descriptor);
878 }
879
880 template <typename V, std::size_t I = 0>
881 V tryVariantAlternatives(unsigned index, [[maybe_unused]] const Descriptor& descriptor)
882 {
883 using namespace impl::reflection;
884
885 if constexpr (I >= std::variant_size_v<V>)
886 {
887 throw FbCppException(
888 "Cannot convert SQL type to any variant alternative at index " + std::to_string(index));
889 }
890 else
891 {
892 using Alt = std::variant_alternative_t<I, V>;
893
894 if constexpr (std::is_same_v<Alt, std::monostate>)
895 return tryVariantAlternatives<V, I + 1>(index, descriptor);
896 else if constexpr (isOpaqueTypeV<Alt>)
897 return tryVariantAlternatives<V, I + 1>(index, descriptor);
898 else
899 {
900 auto opt = get<std::optional<Alt>>(index);
901 return V{std::move(opt.value())};
902 }
903 }
904 }
905
906 // FIXME: floating to integral
907 template <typename T>
908 std::optional<T> getNumber(unsigned index, std::optional<int>& scale, const char* typeName)
909 {
910 const auto& descriptor = getDescriptor(index);
911
912 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
913 return std::nullopt;
914
915 auto data = &message[descriptor.offset];
916#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
917 std::optional<BoostInt128> boostInt128;
918 std::optional<BoostDecFloat16> boostDecFloat16;
919 std::optional<BoostDecFloat34> boostDecFloat34;
920#endif
921
922 // FIXME: Use IUtil
923 switch (descriptor.adjustedType)
924 {
925#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
927 boostInt128.emplace(
928 numericConverter.opaqueInt128ToBoostInt128(*reinterpret_cast<const OpaqueInt128*>(data)));
929 data = reinterpret_cast<const std::byte*>(&boostInt128.value());
930 break;
931
933 boostDecFloat16.emplace(numericConverter.opaqueDecFloat16ToBoostDecFloat16(
934 &statusWrapper, *reinterpret_cast<const OpaqueDecFloat16*>(data)));
935 data = reinterpret_cast<const std::byte*>(&boostDecFloat16.value());
936 break;
937
939 boostDecFloat34.emplace(numericConverter.opaqueDecFloat34ToBoostDecFloat34(
940 &statusWrapper, *reinterpret_cast<const OpaqueDecFloat34*>(data)));
941 data = reinterpret_cast<const std::byte*>(&boostDecFloat34.value());
942 break;
943#endif
944
945 default:
946 break;
947 }
948
949 return convertNumber<T>(descriptor, data, scale, typeName);
950 }
951
952 [[noreturn]] static void throwInvalidType(const char* actualType, DescriptorAdjustedType descriptorType)
953 {
954 throw FbCppException("Invalid type: actual type " + std::string(actualType) + ", descriptor type " +
955 std::to_string(static_cast<unsigned>(descriptorType)));
956 }
957
958 template <typename T>
959 T convertNumber(
960 const Descriptor& descriptor, const std::byte* data, std::optional<int>& toScale, const char* toTypeName)
961 {
962 if (!toScale.has_value())
963 {
964 switch (descriptor.adjustedType)
965 {
970 throwInvalidType(toTypeName, descriptor.adjustedType);
971
972 default:
973 break;
974 }
975
976 toScale = descriptor.scale;
977 }
978
979 switch (descriptor.adjustedType)
980 {
982 return numericConverter.numberToNumber<T>(
983 ScaledInt16{*reinterpret_cast<const std::int16_t*>(data), descriptor.scale}, toScale.value());
984
986 return numericConverter.numberToNumber<T>(
987 ScaledInt32{*reinterpret_cast<const std::int32_t*>(data), descriptor.scale}, toScale.value());
988
990 return numericConverter.numberToNumber<T>(
991 ScaledInt64{*reinterpret_cast<const std::int64_t*>(data), descriptor.scale}, toScale.value());
992
993#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
995 return numericConverter.numberToNumber<T>(
996 ScaledBoostInt128{*reinterpret_cast<const BoostInt128*>(data), descriptor.scale},
997 toScale.value());
998
1000 return numericConverter.numberToNumber<T>(
1001 *reinterpret_cast<const BoostDecFloat16*>(data), toScale.value());
1002
1004 return numericConverter.numberToNumber<T>(
1005 *reinterpret_cast<const BoostDecFloat34*>(data), toScale.value());
1006#endif
1007
1009 return numericConverter.numberToNumber<T>(*reinterpret_cast<const float*>(data), toScale.value());
1010
1012 return numericConverter.numberToNumber<T>(*reinterpret_cast<const double*>(data), toScale.value());
1013
1014 default:
1015 throwInvalidType(toTypeName, descriptor.adjustedType);
1016 }
1017 }
1018
1019 private:
1020 Client* client;
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;
1026 };
1027
1032
1033 template <>
1034 inline std::optional<bool> Row::get<std::optional<bool>>(unsigned index)
1035 {
1036 return getBool(index);
1037 }
1038
1039 template <>
1040 inline std::optional<BlobId> Row::get<std::optional<BlobId>>(unsigned index)
1041 {
1042 return getBlobId(index);
1043 }
1044
1045 template <>
1046 inline std::optional<std::int16_t> Row::get<std::optional<std::int16_t>>(unsigned index)
1047 {
1048 return getInt16(index);
1049 }
1050
1051 template <>
1052 inline std::optional<ScaledInt16> Row::get<std::optional<ScaledInt16>>(unsigned index)
1053 {
1054 return getScaledInt16(index);
1055 }
1056
1057 template <>
1058 inline std::optional<std::int32_t> Row::get<std::optional<std::int32_t>>(unsigned index)
1059 {
1060 return getInt32(index);
1061 }
1062
1063 template <>
1064 inline std::optional<ScaledInt32> Row::get<std::optional<ScaledInt32>>(unsigned index)
1065 {
1066 return getScaledInt32(index);
1067 }
1068
1069 template <>
1070 inline std::optional<std::int64_t> Row::get<std::optional<std::int64_t>>(unsigned index)
1071 {
1072 return getInt64(index);
1073 }
1074
1075 template <>
1076 inline std::optional<ScaledInt64> Row::get<std::optional<ScaledInt64>>(unsigned index)
1077 {
1078 return getScaledInt64(index);
1079 }
1080
1081 template <>
1082 inline std::optional<ScaledOpaqueInt128> Row::get<std::optional<ScaledOpaqueInt128>>(unsigned index)
1083 {
1084 return getScaledOpaqueInt128(index);
1085 }
1086
1087#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1088 template <>
1089 inline std::optional<BoostInt128> Row::get<std::optional<BoostInt128>>(unsigned index)
1090 {
1091 return getBoostInt128(index);
1092 }
1093
1094 template <>
1095 inline std::optional<ScaledBoostInt128> Row::get<std::optional<ScaledBoostInt128>>(unsigned index)
1096 {
1097 return getScaledBoostInt128(index);
1098 }
1099#endif
1100
1101 template <>
1102 inline std::optional<float> Row::get<std::optional<float>>(unsigned index)
1103 {
1104 return getFloat(index);
1105 }
1106
1107 template <>
1108 inline std::optional<double> Row::get<std::optional<double>>(unsigned index)
1109 {
1110 return getDouble(index);
1111 }
1112
1113 template <>
1114 inline std::optional<OpaqueDecFloat16> Row::get<std::optional<OpaqueDecFloat16>>(unsigned index)
1115 {
1116 return getOpaqueDecFloat16(index);
1117 }
1118
1119#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1120 template <>
1121 inline std::optional<BoostDecFloat16> Row::get<std::optional<BoostDecFloat16>>(unsigned index)
1122 {
1123 return getBoostDecFloat16(index);
1124 }
1125#endif
1126
1127 template <>
1128 inline std::optional<OpaqueDecFloat34> Row::get<std::optional<OpaqueDecFloat34>>(unsigned index)
1129 {
1130 return getOpaqueDecFloat34(index);
1131 }
1132
1133#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1134 template <>
1135 inline std::optional<BoostDecFloat34> Row::get<std::optional<BoostDecFloat34>>(unsigned index)
1136 {
1137 return getBoostDecFloat34(index);
1138 }
1139#endif
1140
1141 template <>
1142 inline std::optional<Date> Row::get<std::optional<Date>>(unsigned index)
1143 {
1144 return getDate(index);
1145 }
1146
1147 template <>
1148 inline std::optional<OpaqueDate> Row::get<std::optional<OpaqueDate>>(unsigned index)
1149 {
1150 return getOpaqueDate(index);
1151 }
1152
1153 template <>
1154 inline std::optional<Time> Row::get<std::optional<Time>>(unsigned index)
1155 {
1156 return getTime(index);
1157 }
1158
1159 template <>
1160 inline std::optional<OpaqueTime> Row::get<std::optional<OpaqueTime>>(unsigned index)
1161 {
1162 return getOpaqueTime(index);
1163 }
1164
1165 template <>
1166 inline std::optional<OpaqueTimestamp> Row::get<std::optional<OpaqueTimestamp>>(unsigned index)
1167 {
1168 return getOpaqueTimestamp(index);
1169 }
1170
1171 template <>
1172 inline std::optional<Timestamp> Row::get<std::optional<Timestamp>>(unsigned index)
1173 {
1174 return getTimestamp(index);
1175 }
1176
1177 template <>
1178 inline std::optional<TimeTz> Row::get<std::optional<TimeTz>>(unsigned index)
1179 {
1180 return getTimeTz(index);
1181 }
1182
1183 template <>
1184 inline std::optional<OpaqueTimeTz> Row::get<std::optional<OpaqueTimeTz>>(unsigned index)
1185 {
1186 return getOpaqueTimeTz(index);
1187 }
1188
1189 template <>
1190 inline std::optional<TimestampTz> Row::get<std::optional<TimestampTz>>(unsigned index)
1191 {
1192 return getTimestampTz(index);
1193 }
1194
1195 template <>
1196 inline std::optional<OpaqueTimestampTz> Row::get<std::optional<OpaqueTimestampTz>>(unsigned index)
1197 {
1198 return getOpaqueTimestampTz(index);
1199 }
1200
1201 template <>
1202 inline std::optional<std::string> Row::get<std::optional<std::string>>(unsigned index)
1203 {
1204 return getString(index);
1205 }
1206
1210} // namespace fbcpp
1211
1212
1213#endif // FBCPP_ROW_H
Represents a Firebird blob identifier.
Definition Blob.h:52
ISC_QUAD id
Stores the raw Firebird blob identifier value.
Definition Blob.h:64
Represents a Firebird client library instance.
Definition Client.h:53
Base exception class for all fb-cpp exceptions.
Definition Exception.h:230
A lightweight, non-owning view of a single row's data with typed accessors.
Definition Row.h:66
std::optional< BoostDecFloat16 > getBoostDecFloat16(unsigned index)
Reads a Boost-based 16-digit decimal floating-point column.
Definition Row.h:262
std::optional< std::int32_t > getInt32(unsigned index)
Reads a 32-bit signed integer column.
Definition Row.h:141
std::optional< OpaqueTimestamp > getOpaqueTimestamp(unsigned index)
Reads a raw timestamp column in Firebird's representation.
Definition Row.h:406
T get()
Retrieves all output columns into a user-defined aggregate struct.
Definition Row.h:616
std::optional< Timestamp > getTimestamp(unsigned index)
Reads a timestamp column without timezone.
Definition Row.h:385
std::optional< Time > getTime(unsigned index)
Reads a time-of-day column without timezone.
Definition Row.h:344
std::optional< TimestampTz > getTimestampTz(unsigned index)
Reads a timestamp-with-time-zone column.
Definition Row.h:467
std::optional< ScaledInt16 > getScaledInt16(unsigned index)
Reads a scaled 16-bit signed integer column.
Definition Row.h:131
std::optional< float > getFloat(unsigned index)
Reads a single precision floating-point column.
Definition Row.h:223
Row(Client &client, const std::vector< Descriptor > &descriptors, std::span< const std::byte > message)
Constructs a Row view over the given message buffer.
Definition Row.h:75
std::optional< OpaqueTimestampTz > getOpaqueTimestampTz(unsigned index)
Reads a raw timestamp-with-time-zone column in Firebird's representation.
Definition Row.h:488
std::optional< BoostInt128 > getBoostInt128(unsigned index)
Reads a Boost 128-bit integer column.
Definition Row.h:202
std::optional< TimeTz > getTimeTz(unsigned index)
Reads a time-of-day column with timezone.
Definition Row.h:426
V get(unsigned index)
Retrieves a column value as a user-defined variant type.
Definition Row.h:654
std::optional< OpaqueTimeTz > getOpaqueTimeTz(unsigned index)
Reads a raw time-of-day column with timezone in Firebird's representation.
Definition Row.h:447
std::optional< ScaledOpaqueInt128 > getScaledOpaqueInt128(unsigned index)
Reads a Firebird scaled 128-bit integer column.
Definition Row.h:179
std::optional< std::int64_t > getInt64(unsigned index)
Reads a 64-bit signed integer column.
Definition Row.h:160
std::optional< ScaledBoostInt128 > getScaledBoostInt128(unsigned index)
Reads a scaled Boost 128-bit integer column.
Definition Row.h:212
std::optional< ScaledInt32 > getScaledInt32(unsigned index)
Reads a scaled 32-bit signed integer column.
Definition Row.h:150
std::optional< OpaqueDecFloat16 > getOpaqueDecFloat16(unsigned index)
Reads a Firebird 16-digit decimal floating-point column.
Definition Row.h:241
std::optional< double > getDouble(unsigned index)
Reads a double precision floating-point column.
Definition Row.h:232
bool isNull(unsigned index)
Reports whether the row has a null at the given column.
Definition Row.h:93
std::optional< BlobId > getBlobId(unsigned index)
Reads a blob identifier column.
Definition Row.h:508
std::optional< std::int16_t > getInt16(unsigned index)
Reads a 16-bit signed integer column.
Definition Row.h:122
std::optional< OpaqueDate > getOpaqueDate(unsigned index)
Reads a raw date column in Firebird's representation.
Definition Row.h:324
std::optional< OpaqueDecFloat34 > getOpaqueDecFloat34(unsigned index)
Reads a Firebird 34-digit decimal floating-point column.
Definition Row.h:272
std::optional< Date > getDate(unsigned index)
Reads a date column.
Definition Row.h:303
std::optional< ScaledInt64 > getScaledInt64(unsigned index)
Reads a scaled 64-bit signed integer column.
Definition Row.h:169
std::optional< BoostDecFloat34 > getBoostDecFloat34(unsigned index)
Reads a Boost-based 34-digit decimal floating-point column.
Definition Row.h:293
std::optional< std::string > getString(unsigned index)
Reads a textual column, applying number-to-string conversions when needed.
Definition Row.h:532
std::optional< bool > getBool(unsigned index)
Reads a boolean column.
Definition Row.h:102
std::optional< OpaqueTime > getOpaqueTime(unsigned index)
Reads a raw time-of-day column in Firebird's representation.
Definition Row.h:365
T get(unsigned index)
Retrieves a column using the most appropriate typed accessor specialization.
fb-cpp namespace.
Definition Attachment.h:42
ScaledNumber< std::int64_t > ScaledInt64
Signed 64-bit scaled number.
Definition types.h:79
ScaledNumber< std::int32_t > ScaledInt32
Signed 32-bit scaled number.
Definition types.h:74
boost::multiprecision::number< boost::multiprecision::cpp_dec_float< 34 > > BoostDecFloat34
34-digit decimal floating point using Boost.Multiprecision.
Definition types.h:102
FB_DEC16 OpaqueDecFloat16
Opaque 16-digit decimal floating point exposed by the Firebird API.
Definition types.h:205
ScaledNumber< std::int16_t > ScaledInt16
Signed 16-bit scaled number.
Definition types.h:69
boost::multiprecision::number< boost::multiprecision::cpp_dec_float< 16 > > BoostDecFloat16
16-digit decimal floating point using Boost.Multiprecision.
Definition types.h:97
DescriptorAdjustedType
Descriptor adjusted type.
Definition Descriptor.h:147
@ 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.
Definition types.h:200
FB_DEC34 OpaqueDecFloat34
Opaque 34-digit decimal floating point exposed by the Firebird API.
Definition types.h:210
boost::multiprecision::int128_t BoostInt128
128-bit integer using Boost.Multiprecision.
Definition types.h:85
ScaledNumber< BoostInt128 > ScaledBoostInt128
Scaled 128-bit integer backed by Boost.Multiprecision.
Definition types.h:90
Describes a parameter or column.
Definition Descriptor.h:248
Wrapper for Firebird date values.
Definition types.h:221
Wrapper for Firebird time-with-time-zone values.
Definition types.h:263
Wrapper for Firebird time values.
Definition types.h:234
Wrapper for Firebird timestamp-with-time-zone values.
Definition types.h:279
Wrapper for Firebird timestamp values.
Definition types.h:247
Represents a numeric value with an explicit decimal scale.
Definition types.h:52
int scale
Decimal scale applied to value.
Definition types.h:63
T value
Unscaled numeric value.
Definition types.h:58