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#if FB_CPP_USE_BOOST_DECIMAL != 0
53#include <boost/decimal.hpp>
54#endif
55
56
60namespace fbcpp
61{
69 class Row final
70 {
71 public:
79 Row(Client& client, const std::vector<Descriptor>& descriptors, std::span<const std::byte> message)
80 : client{&client},
81 descriptors{&descriptors},
82 message{message},
83 statusWrapper{client},
84 numericConverter{client},
85 calendarConverter{client}
86 {
87 }
88
89 public:
93
97 bool isNull(unsigned index)
98 {
99 const auto& descriptor = getDescriptor(index);
100 return *reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE;
101 }
102
106 std::optional<bool> getBool(unsigned index)
107 {
108 const auto& descriptor = getDescriptor(index);
109
110 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
111 return std::nullopt;
112
113 switch (descriptor.adjustedType)
114 {
116 return message[descriptor.offset] != std::byte{0};
117
118 default:
119 throwInvalidType("bool", descriptor.adjustedType);
120 }
121 }
122
126 std::optional<std::int16_t> getInt16(unsigned index)
127 {
128 std::optional<int> scale{0};
129 return getNumber<std::int16_t>(index, scale, "std::int16_t");
130 }
131
135 std::optional<ScaledInt16> getScaledInt16(unsigned index)
136 {
137 std::optional<int> scale;
138 const auto value = getNumber<std::int16_t>(index, scale, "ScaledInt16");
139 return value.has_value() ? std::optional{ScaledInt16{value.value(), scale.value()}} : std::nullopt;
140 }
141
145 std::optional<std::int32_t> getInt32(unsigned index)
146 {
147 std::optional<int> scale{0};
148 return getNumber<std::int32_t>(index, scale, "std::int32_t");
149 }
150
154 std::optional<ScaledInt32> getScaledInt32(unsigned index)
155 {
156 std::optional<int> scale;
157 const auto value = getNumber<std::int32_t>(index, scale, "ScaledInt32");
158 return value.has_value() ? std::optional{ScaledInt32{value.value(), scale.value()}} : std::nullopt;
159 }
160
164 std::optional<std::int64_t> getInt64(unsigned index)
165 {
166 std::optional<int> scale{0};
167 return getNumber<std::int64_t>(index, scale, "std::int64_t");
168 }
169
173 std::optional<ScaledInt64> getScaledInt64(unsigned index)
174 {
175 std::optional<int> scale;
176 const auto value = getNumber<std::int64_t>(index, scale, "ScaledInt64");
177 return value.has_value() ? std::optional{ScaledInt64{value.value(), scale.value()}} : std::nullopt;
178 }
179
183 std::optional<ScaledOpaqueInt128> getScaledOpaqueInt128(unsigned index)
184 {
185 const auto& descriptor = getDescriptor(index);
186
187 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
188 return std::nullopt;
189
190 switch (descriptor.adjustedType)
191 {
193 return ScaledOpaqueInt128{
194 OpaqueInt128{*reinterpret_cast<const OpaqueInt128*>(&message[descriptor.offset])},
196
197 default:
198 throwInvalidType("ScaledOpaqueInt128", descriptor.adjustedType);
199 }
200 }
201
202#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
206 std::optional<BoostInt128> getBoostInt128(unsigned index)
207 {
208 std::optional<int> scale{0};
209 const auto value = getNumber<BoostInt128>(index, scale, "BoostInt128");
210 return value.has_value() ? std::optional{value.value()} : std::nullopt;
211 }
212
216 std::optional<ScaledBoostInt128> getScaledBoostInt128(unsigned index)
217 {
218 std::optional<int> scale;
219 const auto value = getNumber<BoostInt128>(index, scale, "ScaledBoostInt128");
220 return value.has_value() ? std::optional{ScaledBoostInt128{value.value(), scale.value()}} : std::nullopt;
221 }
222#endif
223
227 std::optional<float> getFloat(unsigned index)
228 {
229 std::optional<int> scale{0};
230 return getNumber<float>(index, scale, "float");
231 }
232
236 std::optional<double> getDouble(unsigned index)
237 {
238 std::optional<int> scale{0};
239 return getNumber<double>(index, scale, "double");
240 }
241
245 std::optional<OpaqueDecFloat16> getOpaqueDecFloat16(unsigned index)
246 {
247 const auto& descriptor = getDescriptor(index);
248
249 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
250 return std::nullopt;
251
252 switch (descriptor.adjustedType)
253 {
255 return OpaqueDecFloat16{*reinterpret_cast<const OpaqueDecFloat16*>(&message[descriptor.offset])};
256
257 default:
258 throwInvalidType("OpaqueDecFloat16", descriptor.adjustedType);
259 }
260 }
261
262#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
266 std::optional<BoostDecFloat16> getBoostDecFloat16(unsigned index)
267 {
268 std::optional<int> scale{0};
269 return getNumber<BoostDecFloat16>(index, scale, "BoostDecFloat16");
270 }
271#endif
272
273#if FB_CPP_USE_BOOST_DECIMAL != 0
277 std::optional<BoostDecimal32> getBoostDecimal32(unsigned index)
278 {
279 std::optional<int> scale{0};
280 return getNumber<BoostDecimal32>(index, scale, "BoostDecimal32");
281 }
282
286 std::optional<BoostDecimal64> getBoostDecimal64(unsigned index)
287 {
288 std::optional<int> scale{0};
289 return getNumber<BoostDecimal64>(index, scale, "BoostDecimal64");
290 }
291#endif
292
296 std::optional<OpaqueDecFloat34> getOpaqueDecFloat34(unsigned index)
297 {
298 const auto& descriptor = getDescriptor(index);
299
300 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
301 return std::nullopt;
302
303 switch (descriptor.adjustedType)
304 {
306 return OpaqueDecFloat34{*reinterpret_cast<const OpaqueDecFloat34*>(&message[descriptor.offset])};
307
308 default:
309 throwInvalidType("OpaqueDecFloat34", descriptor.adjustedType);
310 }
311 }
312
313#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
317 std::optional<BoostDecFloat34> getBoostDecFloat34(unsigned index)
318 {
319 std::optional<int> scale{0};
320 return getNumber<BoostDecFloat34>(index, scale, "BoostDecFloat34");
321 }
322#endif
323
324#if FB_CPP_USE_BOOST_DECIMAL != 0
328 std::optional<BoostDecimal128> getBoostDecimal128(unsigned index)
329 {
330 std::optional<int> scale{0};
331 return getNumber<BoostDecimal128>(index, scale, "BoostDecimal128");
332 }
333#endif
334
338 std::optional<Date> getDate(unsigned index)
339 {
340 const auto& descriptor = getDescriptor(index);
341
342 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
343 return std::nullopt;
344
345 switch (descriptor.adjustedType)
346 {
348 return calendarConverter.opaqueDateToDate(
349 *reinterpret_cast<const OpaqueDate*>(&message[descriptor.offset]));
350
351 default:
352 throwInvalidType("Date", descriptor.adjustedType);
353 }
354 }
355
359 std::optional<OpaqueDate> getOpaqueDate(unsigned index)
360 {
361 const auto& descriptor = getDescriptor(index);
362
363 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
364 return std::nullopt;
365
366 switch (descriptor.adjustedType)
367 {
369 return OpaqueDate{*reinterpret_cast<const OpaqueDate*>(&message[descriptor.offset])};
370
371 default:
372 throwInvalidType("OpaqueDate", descriptor.adjustedType);
373 }
374 }
375
379 std::optional<Time> getTime(unsigned index)
380 {
381 const auto& descriptor = getDescriptor(index);
382
383 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
384 return std::nullopt;
385
386 switch (descriptor.adjustedType)
387 {
389 return calendarConverter.opaqueTimeToTime(
390 *reinterpret_cast<const OpaqueTime*>(&message[descriptor.offset]));
391
392 default:
393 throwInvalidType("Time", descriptor.adjustedType);
394 }
395 }
396
400 std::optional<OpaqueTime> getOpaqueTime(unsigned index)
401 {
402 const auto& descriptor = getDescriptor(index);
403
404 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
405 return std::nullopt;
406
407 switch (descriptor.adjustedType)
408 {
410 return OpaqueTime{*reinterpret_cast<const OpaqueTime*>(&message[descriptor.offset])};
411
412 default:
413 throwInvalidType("OpaqueTime", descriptor.adjustedType);
414 }
415 }
416
420 std::optional<Timestamp> getTimestamp(unsigned index)
421 {
422 const auto& descriptor = getDescriptor(index);
423
424 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
425 return std::nullopt;
426
427 switch (descriptor.adjustedType)
428 {
430 return calendarConverter.opaqueTimestampToTimestamp(
431 *reinterpret_cast<const OpaqueTimestamp*>(&message[descriptor.offset]));
432
433 default:
434 throwInvalidType("Timestamp", descriptor.adjustedType);
435 }
436 }
437
441 std::optional<OpaqueTimestamp> getOpaqueTimestamp(unsigned index)
442 {
443 const auto& descriptor = getDescriptor(index);
444
445 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
446 return std::nullopt;
447
448 switch (descriptor.adjustedType)
449 {
451 return OpaqueTimestamp{*reinterpret_cast<const OpaqueTimestamp*>(&message[descriptor.offset])};
452
453 default:
454 throwInvalidType("OpaqueTimestamp", descriptor.adjustedType);
455 }
456 }
457
461 std::optional<TimeTz> getTimeTz(unsigned index)
462 {
463 const auto& descriptor = getDescriptor(index);
464
465 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
466 return std::nullopt;
467
468 switch (descriptor.adjustedType)
469 {
471 return calendarConverter.opaqueTimeTzToTimeTz(
472 &statusWrapper, *reinterpret_cast<const OpaqueTimeTz*>(&message[descriptor.offset]));
473
474 default:
475 throwInvalidType("TimeTz", descriptor.adjustedType);
476 }
477 }
478
482 std::optional<OpaqueTimeTz> getOpaqueTimeTz(unsigned index)
483 {
484 const auto& descriptor = getDescriptor(index);
485
486 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
487 return std::nullopt;
488
489 switch (descriptor.adjustedType)
490 {
492 return OpaqueTimeTz{*reinterpret_cast<const OpaqueTimeTz*>(&message[descriptor.offset])};
493
494 default:
495 throwInvalidType("OpaqueTimeTz", descriptor.adjustedType);
496 }
497 }
498
502 std::optional<TimestampTz> getTimestampTz(unsigned index)
503 {
504 const auto& descriptor = getDescriptor(index);
505
506 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
507 return std::nullopt;
508
509 switch (descriptor.adjustedType)
510 {
512 return calendarConverter.opaqueTimestampTzToTimestampTz(
513 &statusWrapper, *reinterpret_cast<const OpaqueTimestampTz*>(&message[descriptor.offset]));
514
515 default:
516 throwInvalidType("TimestampTz", descriptor.adjustedType);
517 }
518 }
519
523 std::optional<OpaqueTimestampTz> getOpaqueTimestampTz(unsigned index)
524 {
525 const auto& descriptor = getDescriptor(index);
526
527 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
528 return std::nullopt;
529
530 switch (descriptor.adjustedType)
531 {
533 return OpaqueTimestampTz{*reinterpret_cast<const OpaqueTimestampTz*>(&message[descriptor.offset])};
534
535 default:
536 throwInvalidType("OpaqueTimestampTz", descriptor.adjustedType);
537 }
538 }
539
543 std::optional<BlobId> getBlobId(unsigned index)
544 {
545 const auto& descriptor = getDescriptor(index);
546
547 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
548 return std::nullopt;
549
550 switch (descriptor.adjustedType)
551 {
553 {
554 BlobId value;
555 value.id = *reinterpret_cast<const ISC_QUAD*>(&message[descriptor.offset]);
556 return value;
557 }
558
559 default:
560 throwInvalidType("BlobId", descriptor.adjustedType);
561 }
562 }
563
567 std::optional<std::string> getString(unsigned index)
568 {
569 const auto& descriptor = getDescriptor(index);
570
571 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
572 return std::nullopt;
573
574 const auto data = &message[descriptor.offset];
575
576 switch (descriptor.adjustedType)
577 {
579 return (message[descriptor.offset] != std::byte{0}) ? std::string{"true"} : std::string{"false"};
580
582 return numericConverter.numberToString(
583 ScaledInt16{*reinterpret_cast<const std::int16_t*>(data), descriptor.scale});
584
586 return numericConverter.numberToString(
587 ScaledInt32{*reinterpret_cast<const std::int32_t*>(data), descriptor.scale});
588
590 return numericConverter.numberToString(
591 ScaledInt64{*reinterpret_cast<const std::int64_t*>(data), descriptor.scale});
592
594 return numericConverter.opaqueInt128ToString(
595 &statusWrapper, *reinterpret_cast<const OpaqueInt128*>(data), descriptor.scale);
596
598 return numericConverter.numberToString(*reinterpret_cast<const float*>(data));
599
601 return numericConverter.numberToString(*reinterpret_cast<const double*>(data));
602
604 return calendarConverter.opaqueDateToString(*reinterpret_cast<const OpaqueDate*>(data));
605
607 return calendarConverter.opaqueTimeToString(*reinterpret_cast<const OpaqueTime*>(data));
608
610 return calendarConverter.opaqueTimestampToString(*reinterpret_cast<const OpaqueTimestamp*>(data));
611
613 return calendarConverter.opaqueTimeTzToString(
614 &statusWrapper, *reinterpret_cast<const OpaqueTimeTz*>(data));
615
617 return calendarConverter.opaqueTimestampTzToString(
618 &statusWrapper, *reinterpret_cast<const OpaqueTimestampTz*>(data));
619
621 return numericConverter.opaqueDecFloat16ToString(
622 &statusWrapper, *reinterpret_cast<const OpaqueDecFloat16*>(data));
623
625 return numericConverter.opaqueDecFloat34ToString(
626 &statusWrapper, *reinterpret_cast<const OpaqueDecFloat34*>(data));
627
629 return std::string{reinterpret_cast<const char*>(data + sizeof(std::uint16_t)),
630 *reinterpret_cast<const std::uint16_t*>(data)};
631
632 default:
633 throwInvalidType("std::string", descriptor.adjustedType);
634 }
635 }
636
640
644 template <typename T>
645 T get(unsigned index);
646
650 template <Aggregate T>
651 T get()
652 {
653 using namespace impl::reflection;
654
655 constexpr std::size_t N = fieldCountV<T>;
656
657 if (N != descriptors->size())
658 {
659 throw FbCppException("Struct field count (" + std::to_string(N) +
660 ") does not match output column count (" + std::to_string(descriptors->size()) + ")");
661 }
662
663 return getStruct<T>(std::make_index_sequence<N>{});
664 }
665
669 template <TupleLike T>
670 T get()
671 {
672 using namespace impl::reflection;
673
674 constexpr std::size_t N = std::tuple_size_v<T>;
675
676 if (N != descriptors->size())
677 {
678 throw FbCppException("Tuple element count (" + std::to_string(N) +
679 ") does not match output column count (" + std::to_string(descriptors->size()) + ")");
680 }
681
682 return getTuple<T>(std::make_index_sequence<N>{});
683 }
684
688 template <VariantLike V>
689 V get(unsigned index)
690 {
691 using namespace impl::reflection;
692
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.");
697
698 const auto& descriptor = getDescriptor(index);
699
700 if (isNull(index))
701 {
703 return V{std::monostate{}};
704 else
705 {
706 throw FbCppException(
707 "NULL value encountered but variant does not contain std::monostate at index " +
708 std::to_string(index));
709 }
710 }
711
712 return getVariantValue<V>(index, descriptor);
713 }
714
715 private:
716 const Descriptor& getDescriptor(unsigned index)
717 {
718 if (index >= descriptors->size())
719 throw std::out_of_range("index out of range");
720
721 return (*descriptors)[index];
722 }
723
724 template <typename T, std::size_t... Is>
725 T getStruct(std::index_sequence<Is...>)
726 {
727 using namespace impl::reflection;
728
729 return T{getStructField<FieldType<T, Is>>(static_cast<unsigned>(Is))...};
730 }
731
732 template <typename F>
733 auto getStructField(unsigned index)
734 {
735 using namespace impl::reflection;
736
737 if constexpr (isOptionalV<F>)
738 return get<F>(index);
739 else if constexpr (isVariantV<F>)
740 return get<F>(index);
741 else
742 {
743 auto opt = get<std::optional<F>>(index);
744
745 if (!opt.has_value())
746 {
747 throw FbCppException(
748 "Null value encountered for non-optional field at index " + std::to_string(index));
749 }
750
751 return std::move(opt.value());
752 }
753 }
754
755 template <typename T, std::size_t... Is>
756 T getTuple(std::index_sequence<Is...>)
757 {
758 using namespace impl::reflection;
759
760 return T{getStructField<std::tuple_element_t<Is, T>>(static_cast<unsigned>(Is))...};
761 }
762
763 template <typename V>
764 V getVariantValue(unsigned index, const Descriptor& descriptor)
765 {
766 using namespace impl::reflection;
767
768 switch (descriptor.adjustedType)
769 {
771 if constexpr (variantContainsV<bool, V>)
772 return V{get<std::optional<bool>>(index).value()};
773 break;
774
776 if (descriptor.scale != 0)
777 {
779 return V{get<std::optional<ScaledInt16>>(index).value()};
781 return V{get<std::optional<ScaledInt32>>(index).value()};
783 return V{get<std::optional<ScaledInt64>>(index).value()};
784#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
786 return V{get<std::optional<ScaledBoostInt128>>(index).value()};
787#endif
788 }
790 return V{get<std::optional<std::int16_t>>(index).value()};
791 break;
792
794 if (descriptor.scale != 0)
795 {
797 return V{get<std::optional<ScaledInt32>>(index).value()};
799 return V{get<std::optional<ScaledInt64>>(index).value()};
800#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
802 return V{get<std::optional<ScaledBoostInt128>>(index).value()};
803#endif
804 }
806 return V{get<std::optional<std::int32_t>>(index).value()};
807 break;
808
810 if (descriptor.scale != 0)
811 {
813 return V{get<std::optional<ScaledInt64>>(index).value()};
814#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
816 return V{get<std::optional<ScaledBoostInt128>>(index).value()};
817#endif
818 }
820 return V{get<std::optional<std::int64_t>>(index).value()};
821 break;
822
825 return V{get<std::optional<ScaledOpaqueInt128>>(index).value()};
826#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
827 else if (descriptor.scale != 0)
828 {
830 return V{get<std::optional<ScaledBoostInt128>>(index).value()};
831 }
832 else if constexpr (variantContainsV<BoostInt128, V>)
833 return V{get<std::optional<BoostInt128>>(index).value()};
834#endif
835 break;
836
838 if constexpr (variantContainsV<float, V>)
839 return V{get<std::optional<float>>(index).value()};
840 break;
841
843 if constexpr (variantContainsV<double, V>)
844 return V{get<std::optional<double>>(index).value()};
845 break;
846
849 return V{get<std::optional<OpaqueDecFloat16>>(index).value()};
850#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
851 else if constexpr (variantContainsV<BoostDecFloat16, V>)
852 return V{get<std::optional<BoostDecFloat16>>(index).value()};
853#endif
854#if FB_CPP_USE_BOOST_DECIMAL != 0
855 else if constexpr (variantContainsV<BoostDecimal64, V>)
856 return V{get<std::optional<BoostDecimal64>>(index).value()};
857#endif
858 break;
859
862 return V{get<std::optional<OpaqueDecFloat34>>(index).value()};
863#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
864 else if constexpr (variantContainsV<BoostDecFloat34, V>)
865 return V{get<std::optional<BoostDecFloat34>>(index).value()};
866#endif
867#if FB_CPP_USE_BOOST_DECIMAL != 0
868 else if constexpr (variantContainsV<BoostDecimal128, V>)
869 return V{get<std::optional<BoostDecimal128>>(index).value()};
870#endif
871 break;
872
875 return V{get<std::optional<std::string>>(index).value()};
876 break;
877
880 return V{get<std::optional<OpaqueDate>>(index).value()};
881 else if constexpr (variantContainsV<Date, V>)
882 return V{get<std::optional<Date>>(index).value()};
883 break;
884
887 return V{get<std::optional<OpaqueTime>>(index).value()};
888 else if constexpr (variantContainsV<Time, V>)
889 return V{get<std::optional<Time>>(index).value()};
890 break;
891
894 return V{get<std::optional<OpaqueTimestamp>>(index).value()};
895 else if constexpr (variantContainsV<Timestamp, V>)
896 return V{get<std::optional<Timestamp>>(index).value()};
897 break;
898
901 return V{get<std::optional<OpaqueTimeTz>>(index).value()};
902 else if constexpr (variantContainsV<TimeTz, V>)
903 return V{get<std::optional<TimeTz>>(index).value()};
904 break;
905
908 return V{get<std::optional<OpaqueTimestampTz>>(index).value()};
909 else if constexpr (variantContainsV<TimestampTz, V>)
910 return V{get<std::optional<TimestampTz>>(index).value()};
911 break;
912
914 if constexpr (variantContainsV<BlobId, V>)
915 return V{get<std::optional<BlobId>>(index).value()};
916 break;
917
918 default:
919 break;
920 }
921
923 }
924
925 template <typename V, std::size_t I = 0>
926 V tryVariantAlternatives(unsigned index, [[maybe_unused]] const Descriptor& descriptor)
927 {
928 using namespace impl::reflection;
929
930 if constexpr (I >= std::variant_size_v<V>)
931 {
932 throw FbCppException(
933 "Cannot convert SQL type to any variant alternative at index " + std::to_string(index));
934 }
935 else
936 {
937 using Alt = std::variant_alternative_t<I, V>;
938
939 if constexpr (std::is_same_v<Alt, std::monostate>)
941 else if constexpr (isOpaqueTypeV<Alt>)
943 else
944 {
945 auto opt = get<std::optional<Alt>>(index);
946 return V{std::move(opt.value())};
947 }
948 }
949 }
950
951 // FIXME: floating to integral
952 template <typename T>
953 std::optional<T> getNumber(unsigned index, std::optional<int>& scale, const char* typeName)
954 {
955 const auto& descriptor = getDescriptor(index);
956
957 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
958 return std::nullopt;
959
960 auto data = &message[descriptor.offset];
961#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
962 std::optional<BoostInt128> boostInt128;
963#endif
964#if FB_CPP_USE_BOOST_DECIMAL != 0
965 std::optional<BoostDecimal64> boostDecimal64;
966 std::optional<BoostDecimal128> boostDecimal128;
967#elif FB_CPP_USE_BOOST_MULTIPRECISION != 0
968 std::optional<BoostDecFloat16> boostDecFloat16;
969 std::optional<BoostDecFloat34> boostDecFloat34;
970#endif
971
972 // FIXME: Use IUtil
973 switch (descriptor.adjustedType)
974 {
975#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
977 boostInt128.emplace(numericConverter.opaqueInt128ToBoostInt128(
978 &statusWrapper, *reinterpret_cast<const OpaqueInt128*>(data)));
979 data = reinterpret_cast<const std::byte*>(&boostInt128.value());
980 break;
981#endif
982
983#if FB_CPP_USE_BOOST_DECIMAL != 0
985 boostDecimal64.emplace(numericConverter.opaqueDecFloat16ToBoostDecimal64(
986 &statusWrapper, *reinterpret_cast<const OpaqueDecFloat16*>(data)));
987 data = reinterpret_cast<const std::byte*>(&boostDecimal64.value());
988 break;
989
991 boostDecimal128.emplace(numericConverter.opaqueDecFloat34ToBoostDecimal128(
992 &statusWrapper, *reinterpret_cast<const OpaqueDecFloat34*>(data)));
993 data = reinterpret_cast<const std::byte*>(&boostDecimal128.value());
994 break;
995#elif FB_CPP_USE_BOOST_MULTIPRECISION != 0
997 boostDecFloat16.emplace(numericConverter.opaqueDecFloat16ToBoostDecFloat16(
998 &statusWrapper, *reinterpret_cast<const OpaqueDecFloat16*>(data)));
999 data = reinterpret_cast<const std::byte*>(&boostDecFloat16.value());
1000 break;
1001
1003 boostDecFloat34.emplace(numericConverter.opaqueDecFloat34ToBoostDecFloat34(
1004 &statusWrapper, *reinterpret_cast<const OpaqueDecFloat34*>(data)));
1005 data = reinterpret_cast<const std::byte*>(&boostDecFloat34.value());
1006 break;
1007#endif
1008
1009 default:
1010 break;
1011 }
1012
1013 return convertNumber<T>(descriptor, data, scale, typeName);
1014 }
1015
1016 [[noreturn]] static void throwInvalidType(const char* actualType, DescriptorAdjustedType descriptorType)
1017 {
1018 throw FbCppException("Invalid type: actual type " + std::string(actualType) + ", descriptor type " +
1019 std::to_string(static_cast<unsigned>(descriptorType)));
1020 }
1021
1022 template <typename T>
1023 T convertNumber(
1024 const Descriptor& descriptor, const std::byte* data, std::optional<int>& toScale, const char* toTypeName)
1025 {
1026 if (!toScale.has_value())
1027 {
1028 switch (descriptor.adjustedType)
1029 {
1034 throwInvalidType(toTypeName, descriptor.adjustedType);
1035
1036 default:
1037 break;
1038 }
1039
1040 toScale = descriptor.scale;
1041 }
1042
1043 switch (descriptor.adjustedType)
1044 {
1046 return numericConverter.numberToNumber<T>(
1047 ScaledInt16{*reinterpret_cast<const std::int16_t*>(data), descriptor.scale}, toScale.value());
1048
1050 return numericConverter.numberToNumber<T>(
1051 ScaledInt32{*reinterpret_cast<const std::int32_t*>(data), descriptor.scale}, toScale.value());
1052
1054 return numericConverter.numberToNumber<T>(
1055 ScaledInt64{*reinterpret_cast<const std::int64_t*>(data), descriptor.scale}, toScale.value());
1056
1057#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1059 return numericConverter.numberToNumber<T>(
1060 ScaledBoostInt128{*reinterpret_cast<const BoostInt128*>(data), descriptor.scale},
1061 toScale.value());
1062#endif
1063
1064#if FB_CPP_USE_BOOST_DECIMAL != 0
1066 return numericConverter.numberToNumber<T>(
1067 *reinterpret_cast<const BoostDecimal64*>(data), toScale.value());
1068
1070 return numericConverter.numberToNumber<T>(
1071 *reinterpret_cast<const BoostDecimal128*>(data), toScale.value());
1072#elif FB_CPP_USE_BOOST_MULTIPRECISION != 0
1074 return numericConverter.numberToNumber<T>(
1075 *reinterpret_cast<const BoostDecFloat16*>(data), toScale.value());
1076
1078 return numericConverter.numberToNumber<T>(
1079 *reinterpret_cast<const BoostDecFloat34*>(data), toScale.value());
1080#endif
1081
1083 return numericConverter.numberToNumber<T>(*reinterpret_cast<const float*>(data), toScale.value());
1084
1086 return numericConverter.numberToNumber<T>(*reinterpret_cast<const double*>(data), toScale.value());
1087
1088 default:
1089 throwInvalidType(toTypeName, descriptor.adjustedType);
1090 }
1091 }
1092
1093 private:
1094 Client* client;
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;
1100 };
1101
1106
1107 template <>
1108 inline std::optional<bool> Row::get<std::optional<bool>>(unsigned index)
1109 {
1110 return getBool(index);
1111 }
1112
1113 template <>
1114 inline std::optional<BlobId> Row::get<std::optional<BlobId>>(unsigned index)
1115 {
1116 return getBlobId(index);
1117 }
1118
1119 template <>
1120 inline std::optional<std::int16_t> Row::get<std::optional<std::int16_t>>(unsigned index)
1121 {
1122 return getInt16(index);
1123 }
1124
1125 template <>
1126 inline std::optional<ScaledInt16> Row::get<std::optional<ScaledInt16>>(unsigned index)
1127 {
1128 return getScaledInt16(index);
1129 }
1130
1131 template <>
1132 inline std::optional<std::int32_t> Row::get<std::optional<std::int32_t>>(unsigned index)
1133 {
1134 return getInt32(index);
1135 }
1136
1137 template <>
1138 inline std::optional<ScaledInt32> Row::get<std::optional<ScaledInt32>>(unsigned index)
1139 {
1140 return getScaledInt32(index);
1141 }
1142
1143 template <>
1144 inline std::optional<std::int64_t> Row::get<std::optional<std::int64_t>>(unsigned index)
1145 {
1146 return getInt64(index);
1147 }
1148
1149 template <>
1150 inline std::optional<ScaledInt64> Row::get<std::optional<ScaledInt64>>(unsigned index)
1151 {
1152 return getScaledInt64(index);
1153 }
1154
1155 template <>
1156 inline std::optional<ScaledOpaqueInt128> Row::get<std::optional<ScaledOpaqueInt128>>(unsigned index)
1157 {
1158 return getScaledOpaqueInt128(index);
1159 }
1160
1161#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1162 template <>
1163 inline std::optional<BoostInt128> Row::get<std::optional<BoostInt128>>(unsigned index)
1164 {
1165 return getBoostInt128(index);
1166 }
1167
1168 template <>
1169 inline std::optional<ScaledBoostInt128> Row::get<std::optional<ScaledBoostInt128>>(unsigned index)
1170 {
1171 return getScaledBoostInt128(index);
1172 }
1173#endif
1174
1175 template <>
1176 inline std::optional<float> Row::get<std::optional<float>>(unsigned index)
1177 {
1178 return getFloat(index);
1179 }
1180
1181 template <>
1182 inline std::optional<double> Row::get<std::optional<double>>(unsigned index)
1183 {
1184 return getDouble(index);
1185 }
1186
1187 template <>
1188 inline std::optional<OpaqueDecFloat16> Row::get<std::optional<OpaqueDecFloat16>>(unsigned index)
1189 {
1190 return getOpaqueDecFloat16(index);
1191 }
1192
1193#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1194 template <>
1195 inline std::optional<BoostDecFloat16> Row::get<std::optional<BoostDecFloat16>>(unsigned index)
1196 {
1197 return getBoostDecFloat16(index);
1198 }
1199#endif
1200
1201#if FB_CPP_USE_BOOST_DECIMAL != 0
1202 template <>
1203 inline std::optional<BoostDecimal32> Row::get<std::optional<BoostDecimal32>>(unsigned index)
1204 {
1205 return getBoostDecimal32(index);
1206 }
1207
1208 template <>
1209 inline std::optional<BoostDecimal64> Row::get<std::optional<BoostDecimal64>>(unsigned index)
1210 {
1211 return getBoostDecimal64(index);
1212 }
1213#endif
1214
1215 template <>
1216 inline std::optional<OpaqueDecFloat34> Row::get<std::optional<OpaqueDecFloat34>>(unsigned index)
1217 {
1218 return getOpaqueDecFloat34(index);
1219 }
1220
1221#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1222 template <>
1223 inline std::optional<BoostDecFloat34> Row::get<std::optional<BoostDecFloat34>>(unsigned index)
1224 {
1225 return getBoostDecFloat34(index);
1226 }
1227#endif
1228
1229#if FB_CPP_USE_BOOST_DECIMAL != 0
1230 template <>
1231 inline std::optional<BoostDecimal128> Row::get<std::optional<BoostDecimal128>>(unsigned index)
1232 {
1233 return getBoostDecimal128(index);
1234 }
1235#endif
1236
1237 template <>
1238 inline std::optional<Date> Row::get<std::optional<Date>>(unsigned index)
1239 {
1240 return getDate(index);
1241 }
1242
1243 template <>
1244 inline std::optional<OpaqueDate> Row::get<std::optional<OpaqueDate>>(unsigned index)
1245 {
1246 return getOpaqueDate(index);
1247 }
1248
1249 template <>
1250 inline std::optional<Time> Row::get<std::optional<Time>>(unsigned index)
1251 {
1252 return getTime(index);
1253 }
1254
1255 template <>
1256 inline std::optional<OpaqueTime> Row::get<std::optional<OpaqueTime>>(unsigned index)
1257 {
1258 return getOpaqueTime(index);
1259 }
1260
1261 template <>
1262 inline std::optional<OpaqueTimestamp> Row::get<std::optional<OpaqueTimestamp>>(unsigned index)
1263 {
1264 return getOpaqueTimestamp(index);
1265 }
1266
1267 template <>
1268 inline std::optional<Timestamp> Row::get<std::optional<Timestamp>>(unsigned index)
1269 {
1270 return getTimestamp(index);
1271 }
1272
1273 template <>
1274 inline std::optional<TimeTz> Row::get<std::optional<TimeTz>>(unsigned index)
1275 {
1276 return getTimeTz(index);
1277 }
1278
1279 template <>
1280 inline std::optional<OpaqueTimeTz> Row::get<std::optional<OpaqueTimeTz>>(unsigned index)
1281 {
1282 return getOpaqueTimeTz(index);
1283 }
1284
1285 template <>
1286 inline std::optional<TimestampTz> Row::get<std::optional<TimestampTz>>(unsigned index)
1287 {
1288 return getTimestampTz(index);
1289 }
1290
1291 template <>
1292 inline std::optional<OpaqueTimestampTz> Row::get<std::optional<OpaqueTimestampTz>>(unsigned index)
1293 {
1294 return getOpaqueTimestampTz(index);
1295 }
1296
1297 template <>
1298 inline std::optional<std::string> Row::get<std::optional<std::string>>(unsigned index)
1299 {
1300 return getString(index);
1301 }
1302
1306} // namespace fbcpp
1307
1308
1309#endif // FBCPP_ROW_H
Represents a Firebird blob identifier.
Definition Blob.h:53
ISC_QUAD id
Stores the raw Firebird blob identifier value.
Definition Blob.h:65
Represents a Firebird client library instance.
Definition Client.h:54
Base exception class for all fb-cpp exceptions.
Definition Exception.h:231
A lightweight, non-owning view of a single row's data with typed accessors.
Definition Row.h:70
std::optional< BoostDecFloat16 > getBoostDecFloat16(unsigned index)
Reads a Boost-based 16-digit decimal floating-point column.
Definition Row.h:266
std::optional< std::int32_t > getInt32(unsigned index)
Reads a 32-bit signed integer column.
Definition Row.h:145
std::optional< OpaqueTimestamp > getOpaqueTimestamp(unsigned index)
Reads a raw timestamp column in Firebird's representation.
Definition Row.h:441
T get()
Retrieves all output columns into a user-defined aggregate struct.
Definition Row.h:651
std::optional< Timestamp > getTimestamp(unsigned index)
Reads a timestamp column without timezone.
Definition Row.h:420
std::optional< Time > getTime(unsigned index)
Reads a time-of-day column without timezone.
Definition Row.h:379
std::optional< TimestampTz > getTimestampTz(unsigned index)
Reads a timestamp-with-time-zone column.
Definition Row.h:502
std::optional< ScaledInt16 > getScaledInt16(unsigned index)
Reads a scaled 16-bit signed integer column.
Definition Row.h:135
std::optional< float > getFloat(unsigned index)
Reads a single precision floating-point column.
Definition Row.h:227
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:79
std::optional< OpaqueTimestampTz > getOpaqueTimestampTz(unsigned index)
Reads a raw timestamp-with-time-zone column in Firebird's representation.
Definition Row.h:523
std::optional< BoostInt128 > getBoostInt128(unsigned index)
Reads a Boost 128-bit integer column.
Definition Row.h:206
std::optional< TimeTz > getTimeTz(unsigned index)
Reads a time-of-day column with timezone.
Definition Row.h:461
V get(unsigned index)
Retrieves a column value as a user-defined variant type.
Definition Row.h:689
std::optional< OpaqueTimeTz > getOpaqueTimeTz(unsigned index)
Reads a raw time-of-day column with timezone in Firebird's representation.
Definition Row.h:482
std::optional< ScaledOpaqueInt128 > getScaledOpaqueInt128(unsigned index)
Reads a Firebird scaled 128-bit integer column.
Definition Row.h:183
std::optional< std::int64_t > getInt64(unsigned index)
Reads a 64-bit signed integer column.
Definition Row.h:164
std::optional< ScaledBoostInt128 > getScaledBoostInt128(unsigned index)
Reads a scaled Boost 128-bit integer column.
Definition Row.h:216
std::optional< ScaledInt32 > getScaledInt32(unsigned index)
Reads a scaled 32-bit signed integer column.
Definition Row.h:154
std::optional< BoostDecimal64 > getBoostDecimal64(unsigned index)
Reads a Boost.Decimal 16-digit decimal floating-point column.
Definition Row.h:286
std::optional< OpaqueDecFloat16 > getOpaqueDecFloat16(unsigned index)
Reads a Firebird 16-digit decimal floating-point column.
Definition Row.h:245
std::optional< double > getDouble(unsigned index)
Reads a double precision floating-point column.
Definition Row.h:236
std::optional< BoostDecimal32 > getBoostDecimal32(unsigned index)
Reads a Boost.Decimal 7-digit decimal floating-point column.
Definition Row.h:277
bool isNull(unsigned index)
Reports whether the row has a null at the given column.
Definition Row.h:97
std::optional< BlobId > getBlobId(unsigned index)
Reads a blob identifier column.
Definition Row.h:543
std::optional< std::int16_t > getInt16(unsigned index)
Reads a 16-bit signed integer column.
Definition Row.h:126
std::optional< OpaqueDate > getOpaqueDate(unsigned index)
Reads a raw date column in Firebird's representation.
Definition Row.h:359
std::optional< OpaqueDecFloat34 > getOpaqueDecFloat34(unsigned index)
Reads a Firebird 34-digit decimal floating-point column.
Definition Row.h:296
std::optional< Date > getDate(unsigned index)
Reads a date column.
Definition Row.h:338
std::optional< ScaledInt64 > getScaledInt64(unsigned index)
Reads a scaled 64-bit signed integer column.
Definition Row.h:173
std::optional< BoostDecFloat34 > getBoostDecFloat34(unsigned index)
Reads a Boost-based 34-digit decimal floating-point column.
Definition Row.h:317
std::optional< std::string > getString(unsigned index)
Reads a textual column, applying number-to-string conversions when needed.
Definition Row.h:567
std::optional< bool > getBool(unsigned index)
Reads a boolean column.
Definition Row.h:106
std::optional< OpaqueTime > getOpaqueTime(unsigned index)
Reads a raw time-of-day column in Firebird's representation.
Definition Row.h:400
std::optional< BoostDecimal128 > getBoostDecimal128(unsigned index)
Reads a Boost.Decimal 34-digit decimal floating-point column.
Definition Row.h:328
T get(unsigned index)
Retrieves a column using the most appropriate typed accessor specialization.
fb-cpp namespace.
Definition Attachment.h:46
ScaledNumber< std::int64_t > ScaledInt64
Signed 64-bit scaled number.
Definition types.h:93
ScaledNumber< std::int32_t > ScaledInt32
Signed 32-bit scaled number.
Definition types.h:88
boost::multiprecision::number< boost::multiprecision::cpp_dec_float< 34 > > BoostDecFloat34
34-digit decimal floating point using Boost.Multiprecision.
Definition types.h:116
FB_DEC16 OpaqueDecFloat16
Opaque 16-digit decimal floating point exposed by the Firebird API.
Definition types.h:236
ScaledNumber< std::int16_t > ScaledInt16
Signed 16-bit scaled number.
Definition types.h:83
boost::multiprecision::number< boost::multiprecision::cpp_dec_float< 16 > > BoostDecFloat16
16-digit decimal floating point using Boost.Multiprecision.
Definition types.h:111
boost::decimal::decimal128_t BoostDecimal128
34-digit decimal floating point using Boost.Decimal.
Definition types.h:133
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:231
FB_DEC34 OpaqueDecFloat34
Opaque 34-digit decimal floating point exposed by the Firebird API.
Definition types.h:241
boost::multiprecision::int128_t BoostInt128
128-bit integer using Boost.Multiprecision.
Definition types.h:99
ScaledNumber< BoostInt128 > ScaledBoostInt128
Scaled 128-bit integer backed by Boost.Multiprecision.
Definition types.h:104
boost::decimal::decimal64_t BoostDecimal64
16-digit decimal floating point using Boost.Decimal.
Definition types.h:128
Describes a parameter or column.
Definition Descriptor.h:248
Wrapper for Firebird date values.
Definition types.h:252
Wrapper for Firebird time-with-time-zone values.
Definition types.h:294
Wrapper for Firebird time values.
Definition types.h:265
Wrapper for Firebird timestamp-with-time-zone values.
Definition types.h:310
Wrapper for Firebird timestamp values.
Definition types.h:278
Represents a numeric value with an explicit decimal scale.
Definition types.h:57
int scale
Decimal scale applied to value.
Definition types.h:77
T value
Unscaled numeric value.
Definition types.h:72