fb-cpp 0.0.2
A modern C++ wrapper for the Firebird database API
Loading...
Searching...
No Matches
Statement.h
1/*
2 * MIT License
3 *
4 * Copyright (c) 2025 Adriano dos Santos Fernandes
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_STATEMENT_H
26#define FBCPP_STATEMENT_H
27
28#include "config.h"
29#include "fb-api.h"
30#include "types.h"
31#include "Blob.h"
32#include "Attachment.h"
33#include "Client.h"
34#include "NumericConverter.h"
35#include "CalendarConverter.h"
36#include "Descriptor.h"
37#include "SmartPtrs.h"
38#include "Exception.h"
39#include "StructBinding.h"
40#include "VariantTypeTraits.h"
41#include <charconv>
42#include <cerrno>
43#include <cstdlib>
44#include <limits>
45#include <memory>
46#include <optional>
47#include <stdexcept>
48#include <string>
49#include <string_view>
50#include <type_traits>
51#include <vector>
52#include <cassert>
53#include <cmath>
54#include <cstddef>
55#include <cstdint>
56
57#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
58#include <boost/multiprecision/cpp_int.hpp>
59#include <boost/multiprecision/cpp_dec_float.hpp>
60#endif
61
65namespace fbcpp
66{
67 class Transaction;
68
72 class StatementOptions final
73 {
74 public:
79 {
80 return prefetchLegacyPlan;
81 }
82
89 {
90 prefetchLegacyPlan = value;
91 return *this;
92 }
93
97 bool getPrefetchPlan() const
98 {
99 return prefetchPlan;
100 }
101
108 {
109 prefetchPlan = value;
110 return *this;
111 }
112
116 const std::optional<std::string>& getCursorName() const
117 {
118 return cursorName;
119 }
120
126 StatementOptions& setCursorName(const std::string& value)
127 {
128 cursorName = value;
129 return *this;
130 }
131
132 private:
133 bool prefetchLegacyPlan = false;
134 bool prefetchPlan = false;
135 std::optional<std::string> cursorName;
136 };
137
141 enum class StatementType : unsigned
142 {
146 SELECT = isc_info_sql_stmt_select,
150 INSERT = isc_info_sql_stmt_insert,
154 UPDATE = isc_info_sql_stmt_update,
158 DELETE = isc_info_sql_stmt_delete,
162 DDL = isc_info_sql_stmt_ddl,
166 GET_SEGMENT = isc_info_sql_stmt_get_segment,
170 PUT_SEGMENT = isc_info_sql_stmt_put_segment,
174 EXEC_PROCEDURE = isc_info_sql_stmt_exec_procedure,
178 START_TRANSACTION = isc_info_sql_stmt_start_trans,
182 COMMIT = isc_info_sql_stmt_commit,
186 ROLLBACK = isc_info_sql_stmt_rollback,
190 SELECT_FOR_UPDATE = isc_info_sql_stmt_select_for_upd,
194 SET_GENERATOR = isc_info_sql_stmt_set_generator,
198 SAVEPOINT = isc_info_sql_stmt_savepoint,
199 };
200
204 class Statement final
205 {
206 public:
214 explicit Statement(Attachment& attachment, Transaction& transaction, std::string_view sql,
215 const StatementOptions& options = {});
216
220 Statement(Statement&& o) noexcept
221 : attachment{o.attachment},
222 status{std::move(o.status)},
223 statusWrapper{std::move(o.statusWrapper)},
224 calendarConverter{std::move(o.calendarConverter)},
225 numericConverter{std::move(o.numericConverter)},
226 statementHandle{std::move(o.statementHandle)},
227 resultSetHandle{std::move(o.resultSetHandle)},
228 inMetadata{std::move(o.inMetadata)},
229 inDescriptors{std::move(o.inDescriptors)},
230 inMessage{std::move(o.inMessage)},
231 outMetadata{std::move(o.outMetadata)},
232 outDescriptors{std::move(o.outDescriptors)},
233 outMessage{std::move(o.outMessage)},
234 type{o.type}
235 {
236 }
237
238 Statement& operator=(Statement&&) = delete;
239 Statement(const Statement&) = delete;
240 Statement& operator=(const Statement&) = delete;
241
245 ~Statement() noexcept
246 {
247 if (isValid())
248 {
249 try
250 {
251 free();
252 }
253 catch (...)
254 {
255 // swallow
256 }
257 }
258 }
259
260 public:
266
270 bool isValid() noexcept
271 {
272 return statementHandle != nullptr;
273 }
274
280 {
281 return statementHandle;
282 }
283
289 {
290 return resultSetHandle;
291 }
292
297 {
298 return inMetadata;
299 }
300
305 {
306 return outMetadata;
307 }
308
313 {
314 return type;
315 }
316
320
326
330 const std::vector<Descriptor>& getInputDescriptors() noexcept
331 {
332 return inDescriptors;
333 }
334
338 const std::vector<Descriptor>& getOutputDescriptors() noexcept
339 {
340 return outDescriptors;
341 }
342
346
350 void free();
351
355 std::string getLegacyPlan();
356
360 std::string getPlan();
361
367 bool execute(Transaction& transaction);
368
372
376 bool fetchNext();
377
381 bool fetchPrior();
382
386 bool fetchFirst();
387
391 bool fetchLast();
392
396 bool fetchAbsolute(unsigned position);
397
401 bool fetchRelative(int offset);
402
406
410
415 {
416 assert(isValid());
417
418 const auto message = inMessage.data();
419
420 for (const auto& descriptor : inDescriptors)
421 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_TRUE;
422 }
423
428 void setNull(unsigned index)
429 {
430 assert(isValid());
431
432 const auto& descriptor = getInDescriptor(index);
433 const auto message = inMessage.data();
434
435 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_TRUE;
436 }
437
443 void setBool(unsigned index, std::optional<bool> optValue)
444 {
445 if (!optValue.has_value())
446 {
447 setNull(index);
448 return;
449 }
450
451 assert(isValid());
452
453 const auto& value = optValue.value();
454 const auto& descriptor = getInDescriptor(index);
455 const auto message = inMessage.data();
456
457 switch (descriptor.adjustedType)
458 {
460 message[descriptor.offset] = value ? std::byte{1} : std::byte{0};
461 break;
462
463 default:
464 throwInvalidType("bool", descriptor.adjustedType);
465 }
466
467 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_FALSE;
468 }
469
473 void setInt16(unsigned index, std::optional<std::int16_t> optValue)
474 {
475 if (!optValue.has_value())
476 {
477 setNull(index);
478 return;
479 }
480
481 setNumber(index, DescriptorAdjustedType::INT16, optValue.value(), 0, "std::int16_t");
482 }
483
487 void setScaledInt16(unsigned index, std::optional<ScaledInt16> optValue)
488 {
489 if (!optValue.has_value())
490 {
491 setNull(index);
492 return;
493 }
494
495 const auto& value = optValue.value();
496 setNumber(index, DescriptorAdjustedType::INT16, value.value, value.scale, "ScaledInt16");
497 }
498
502 void setInt32(unsigned index, std::optional<std::int32_t> optValue)
503 {
504 if (!optValue.has_value())
505 {
506 setNull(index);
507 return;
508 }
509
510 setNumber(index, DescriptorAdjustedType::INT32, optValue.value(), 0, "std::int32_t");
511 }
512
516 void setScaledInt32(unsigned index, std::optional<ScaledInt32> optValue)
517 {
518 if (!optValue.has_value())
519 {
520 setNull(index);
521 return;
522 }
523
524 const auto& value = optValue.value();
525 setNumber(index, DescriptorAdjustedType::INT32, value.value, value.scale, "ScaledInt32");
526 }
527
531 void setInt64(unsigned index, std::optional<std::int64_t> optValue)
532 {
533 if (!optValue.has_value())
534 {
535 setNull(index);
536 return;
537 }
538
539 setNumber(index, DescriptorAdjustedType::INT64, optValue.value(), 0, "std::int64_t");
540 }
541
545 void setScaledInt64(unsigned index, std::optional<ScaledInt64> optValue)
546 {
547 if (!optValue.has_value())
548 {
549 setNull(index);
550 return;
551 }
552
553 const auto& value = optValue.value();
554 setNumber(index, DescriptorAdjustedType::INT64, value.value, value.scale, "ScaledInt64");
555 }
556
560 void setOpaqueInt128(unsigned index, std::optional<OpaqueInt128> optValue)
561 {
562 if (!optValue.has_value())
563 {
564 setNull(index);
565 return;
566 }
567
568 assert(isValid());
569
570 const auto& value = optValue.value();
571 const auto& descriptor = getInDescriptor(index);
572 const auto message = inMessage.data();
573
574 switch (descriptor.adjustedType)
575 {
577 *reinterpret_cast<OpaqueInt128*>(&message[descriptor.offset]) = value;
578 break;
579
580 default:
581 throwInvalidType("OpaqueInt128", descriptor.adjustedType);
582 }
583
584 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_FALSE;
585 }
586
587#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
591 void setBoostInt128(unsigned index, std::optional<BoostInt128> optValue)
592 {
593 if (!optValue.has_value())
594 {
595 setNull(index);
596 return;
597 }
598
599 setNumber(index, DescriptorAdjustedType::INT128, optValue.value(), 0, "BoostInt128");
600 }
601
605 void setScaledBoostInt128(unsigned index, std::optional<ScaledBoostInt128> optValue)
606 {
607 if (!optValue.has_value())
608 {
609 setNull(index);
610 return;
611 }
612
613 const auto& value = optValue.value();
614 setNumber(index, DescriptorAdjustedType::INT128, value.value, value.scale, "ScaledBoostInt128");
615 }
616#endif
617
621 void setFloat(unsigned index, std::optional<float> optValue)
622 {
623 if (!optValue.has_value())
624 {
625 setNull(index);
626 return;
627 }
628
629 setNumber(index, DescriptorAdjustedType::FLOAT, optValue.value(), 0, "float");
630 }
631
635 void setDouble(unsigned index, std::optional<double> optValue)
636 {
637 if (!optValue.has_value())
638 {
639 setNull(index);
640 return;
641 }
642
643 setNumber(index, DescriptorAdjustedType::DOUBLE, optValue.value(), 0, "double");
644 }
645
649 void setOpaqueDecFloat16(unsigned index, std::optional<OpaqueDecFloat16> optValue)
650 {
651 if (!optValue.has_value())
652 {
653 setNull(index);
654 return;
655 }
656
657 assert(isValid());
658
659 const auto& value = optValue.value();
660 const auto& descriptor = getInDescriptor(index);
661 const auto message = inMessage.data();
662
663 switch (descriptor.adjustedType)
664 {
666 *reinterpret_cast<OpaqueDecFloat16*>(&message[descriptor.offset]) = value;
667 break;
668
669 default:
670 throwInvalidType("OpaqueDecFloat16", descriptor.adjustedType);
671 }
672
673 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_FALSE;
674 }
675
676#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
680 void setBoostDecFloat16(unsigned index, std::optional<BoostDecFloat16> optValue)
681 {
682 if (!optValue.has_value())
683 {
684 setNull(index);
685 return;
686 }
687
688 setNumber(index, DescriptorAdjustedType::DECFLOAT16, optValue.value(), 0, "BoostDecFloat16");
689 }
690#endif
691
695 void setOpaqueDecFloat34(unsigned index, std::optional<OpaqueDecFloat34> optValue)
696 {
697 if (!optValue.has_value())
698 {
699 setNull(index);
700 return;
701 }
702
703 assert(isValid());
704
705 const auto& value = optValue.value();
706 const auto& descriptor = getInDescriptor(index);
707 const auto message = inMessage.data();
708
709 switch (descriptor.adjustedType)
710 {
712 *reinterpret_cast<OpaqueDecFloat34*>(&message[descriptor.offset]) = value;
713 break;
714
715 default:
716 throwInvalidType("OpaqueDecFloat34", descriptor.adjustedType);
717 }
718
719 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_FALSE;
720 }
721
722#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
726 void setBoostDecFloat34(unsigned index, std::optional<BoostDecFloat34> optValue)
727 {
728 if (!optValue.has_value())
729 {
730 setNull(index);
731 return;
732 }
733
734 setNumber(index, DescriptorAdjustedType::DECFLOAT34, optValue.value(), 0, "BoostDecFloat34");
735 }
736#endif
737
741 void setDate(unsigned index, std::optional<Date> optValue)
742 {
743 if (!optValue.has_value())
744 {
745 setNull(index);
746 return;
747 }
748
749 assert(isValid());
750
751 const auto& value = optValue.value();
752 const auto& descriptor = getInDescriptor(index);
753 const auto message = inMessage.data();
754
755 switch (descriptor.adjustedType)
756 {
758 *reinterpret_cast<OpaqueDate*>(&message[descriptor.offset]) =
759 calendarConverter.dateToOpaqueDate(value);
760 break;
761
762 default:
763 throwInvalidType("Date", descriptor.adjustedType);
764 }
765
766 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_FALSE;
767 }
768
772 void setOpaqueDate(unsigned index, std::optional<OpaqueDate> optValue)
773 {
774 if (!optValue.has_value())
775 {
776 setNull(index);
777 return;
778 }
779
780 assert(isValid());
781
782 const auto& value = optValue.value();
783 const auto& descriptor = getInDescriptor(index);
784 const auto message = inMessage.data();
785
786 switch (descriptor.adjustedType)
787 {
789 *reinterpret_cast<OpaqueDate*>(&message[descriptor.offset]) = value;
790 break;
791
792 default:
793 throwInvalidType("OpaqueDate", descriptor.adjustedType);
794 }
795
796 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_FALSE;
797 }
798
802 void setTime(unsigned index, std::optional<Time> optValue)
803 {
804 if (!optValue.has_value())
805 {
806 setNull(index);
807 return;
808 }
809
810 assert(isValid());
811
812 const auto& value = optValue.value();
813 const auto& descriptor = getInDescriptor(index);
814 const auto message = inMessage.data();
815
816 switch (descriptor.adjustedType)
817 {
819 *reinterpret_cast<OpaqueTime*>(&message[descriptor.offset]) =
820 calendarConverter.timeToOpaqueTime(value);
821 break;
822
823 default:
824 throwInvalidType("Time", descriptor.adjustedType);
825 }
826
827 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_FALSE;
828 }
829
833 void setOpaqueTime(unsigned index, std::optional<OpaqueTime> optValue)
834 {
835 if (!optValue.has_value())
836 {
837 setNull(index);
838 return;
839 }
840
841 assert(isValid());
842
843 const auto& value = optValue.value();
844 const auto& descriptor = getInDescriptor(index);
845 const auto message = inMessage.data();
846
847 switch (descriptor.adjustedType)
848 {
850 *reinterpret_cast<OpaqueTime*>(&message[descriptor.offset]) = value;
851 break;
852
853 default:
854 throwInvalidType("OpaqueTime", descriptor.adjustedType);
855 }
856
857 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_FALSE;
858 }
859
863 void setTimestamp(unsigned index, std::optional<Timestamp> optValue)
864 {
865 if (!optValue.has_value())
866 {
867 setNull(index);
868 return;
869 }
870
871 assert(isValid());
872
873 const auto& value = optValue.value();
874 const auto& descriptor = getInDescriptor(index);
875 const auto message = inMessage.data();
876
877 switch (descriptor.adjustedType)
878 {
880 *reinterpret_cast<OpaqueTimestamp*>(&message[descriptor.offset]) =
881 calendarConverter.timestampToOpaqueTimestamp(value);
882 break;
883
884 default:
885 throwInvalidType("Timestamp", descriptor.adjustedType);
886 }
887
888 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_FALSE;
889 }
890
894 void setOpaqueTimestamp(unsigned index, std::optional<OpaqueTimestamp> optValue)
895 {
896 if (!optValue.has_value())
897 {
898 setNull(index);
899 return;
900 }
901
902 assert(isValid());
903
904 const auto& value = optValue.value();
905 const auto& descriptor = getInDescriptor(index);
906 const auto message = inMessage.data();
907
908 switch (descriptor.adjustedType)
909 {
911 *reinterpret_cast<OpaqueTimestamp*>(&message[descriptor.offset]) = value;
912 break;
913
914 default:
915 throwInvalidType("OpaqueTimestamp", descriptor.adjustedType);
916 }
917
918 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_FALSE;
919 }
920
924 void setTimeTz(unsigned index, std::optional<TimeTz> optValue)
925 {
926 if (!optValue.has_value())
927 {
928 setNull(index);
929 return;
930 }
931
932 assert(isValid());
933
934 const auto& value = optValue.value();
935 const auto& descriptor = getInDescriptor(index);
936 auto* const message = inMessage.data();
937
938 switch (descriptor.adjustedType)
939 {
941 *reinterpret_cast<OpaqueTimeTz*>(&message[descriptor.offset]) =
942 calendarConverter.timeTzToOpaqueTimeTz(value);
943 break;
944
945 default:
946 throwInvalidType("TimeTz", descriptor.adjustedType);
947 }
948
949 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_FALSE;
950 }
951
955 void setOpaqueTimeTz(unsigned index, std::optional<OpaqueTimeTz> optValue)
956 {
957 if (!optValue.has_value())
958 {
959 setNull(index);
960 return;
961 }
962
963 assert(isValid());
964
965 const auto& value = optValue.value();
966 const auto& descriptor = getInDescriptor(index);
967 auto* const message = inMessage.data();
968
969 switch (descriptor.adjustedType)
970 {
972 *reinterpret_cast<OpaqueTimeTz*>(&message[descriptor.offset]) = value;
973 break;
974
975 default:
976 throwInvalidType("OpaqueTimeTz", descriptor.adjustedType);
977 }
978
979 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_FALSE;
980 }
981
985 void setTimestampTz(unsigned index, std::optional<TimestampTz> optValue)
986 {
987 if (!optValue.has_value())
988 {
989 setNull(index);
990 return;
991 }
992
993 assert(isValid());
994
995 const auto& value = optValue.value();
996 const auto& descriptor = getInDescriptor(index);
997 auto* const message = inMessage.data();
998
999 switch (descriptor.adjustedType)
1000 {
1002 *reinterpret_cast<OpaqueTimestampTz*>(&message[descriptor.offset]) =
1003 calendarConverter.timestampTzToOpaqueTimestampTz(value);
1004 break;
1005
1006 default:
1007 throwInvalidType("TimestampTz", descriptor.adjustedType);
1008 }
1009
1010 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_FALSE;
1011 }
1012
1016 void setOpaqueTimestampTz(unsigned index, std::optional<OpaqueTimestampTz> optValue)
1017 {
1018 if (!optValue.has_value())
1019 {
1020 setNull(index);
1021 return;
1022 }
1023
1024 assert(isValid());
1025
1026 const auto& value = optValue.value();
1027 const auto& descriptor = getInDescriptor(index);
1028 auto* const message = inMessage.data();
1029
1030 switch (descriptor.adjustedType)
1031 {
1033 *reinterpret_cast<OpaqueTimestampTz*>(&message[descriptor.offset]) = value;
1034 break;
1035
1036 default:
1037 throwInvalidType("OpaqueTimestampTz", descriptor.adjustedType);
1038 }
1039
1040 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_FALSE;
1041 }
1042
1046 void setString(unsigned index, std::optional<std::string_view> optValue)
1047 {
1048 if (!optValue.has_value())
1049 {
1050 setNull(index);
1051 return;
1052 }
1053
1054 assert(isValid());
1055
1056 auto& client = attachment.getClient();
1057 const auto value = optValue.value();
1058 const auto& descriptor = getInDescriptor(index);
1059 const auto message = inMessage.data();
1060 const auto data = &message[descriptor.offset];
1061
1062 switch (descriptor.adjustedType)
1063 {
1065 message[descriptor.offset] = numericConverter.stringToBoolean(value);
1066 break;
1067
1071 {
1072 std::string strValue(value);
1073 int scale = 0;
1074
1075 if (const auto dotPos = strValue.find_last_of('.'); dotPos != std::string_view::npos)
1076 {
1077 for (auto pos = dotPos + 1; pos < strValue.size(); ++pos)
1078 {
1079 const char c = value[pos];
1080
1081 if (c < '0' || c > '9')
1082 break;
1083
1084 --scale;
1085 }
1086
1087 strValue.erase(dotPos, 1);
1088 }
1089
1090 static_assert(sizeof(long long) == sizeof(std::int64_t));
1091 std::int64_t intValue;
1092 const auto convResult =
1093 std::from_chars(strValue.data(), strValue.data() + strValue.size(), intValue);
1094 if (convResult.ec != std::errc{} || convResult.ptr != strValue.data() + strValue.size())
1095 numericConverter.throwConversionErrorFromString(strValue);
1096 auto scaledValue = ScaledInt64{intValue, scale};
1097
1098 if (scale != descriptor.scale)
1099 {
1100 scaledValue.value =
1101 numericConverter.numberToNumber<std::int64_t>(scaledValue, descriptor.scale);
1102 scaledValue.scale = descriptor.scale;
1103 }
1104
1105 setScaledInt64(index, scaledValue);
1106 return;
1107 }
1108
1110 {
1111 std::string strValue(value);
1112 client.getInt128Util(&statusWrapper)
1113 ->fromString(
1114 &statusWrapper, descriptor.scale, strValue.c_str(), reinterpret_cast<OpaqueInt128*>(data));
1115 break;
1116 }
1117
1120 {
1121 double doubleValue;
1122#if defined(__APPLE__)
1123 errno = 0;
1124 std::string valueString{value};
1125 char* parseEnd = nullptr;
1126 doubleValue = std::strtod(valueString.c_str(), &parseEnd);
1127 if (parseEnd != valueString.c_str() + valueString.size() || errno == ERANGE)
1128 numericConverter.throwConversionErrorFromString(std::move(valueString));
1129#else
1130 const auto convResult = std::from_chars(value.data(), value.data() + value.size(), doubleValue);
1131 if (convResult.ec != std::errc{} || convResult.ptr != value.data() + value.size())
1132 numericConverter.throwConversionErrorFromString(std::string{value});
1133#endif
1134 setDouble(index, doubleValue);
1135 return;
1136 }
1137
1139 *reinterpret_cast<OpaqueDate*>(data) = calendarConverter.stringToOpaqueDate(value);
1140 break;
1141
1143 *reinterpret_cast<OpaqueTime*>(data) = calendarConverter.stringToOpaqueTime(value);
1144 break;
1145
1147 *reinterpret_cast<OpaqueTimestamp*>(data) = calendarConverter.stringToOpaqueTimestamp(value);
1148 break;
1149
1151 *reinterpret_cast<OpaqueTimeTz*>(data) = calendarConverter.stringToOpaqueTimeTz(value);
1152 break;
1153
1155 *reinterpret_cast<OpaqueTimestampTz*>(data) = calendarConverter.stringToOpaqueTimestampTz(value);
1156 break;
1157
1158#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1160 {
1161 std::string strValue{value};
1162 client.getDecFloat16Util(&statusWrapper)
1163 ->fromString(&statusWrapper, strValue.c_str(), reinterpret_cast<OpaqueDecFloat16*>(data));
1164 break;
1165 }
1166
1168 {
1169 std::string strValue{value};
1170 client.getDecFloat34Util(&statusWrapper)
1171 ->fromString(&statusWrapper, strValue.c_str(), reinterpret_cast<OpaqueDecFloat34*>(data));
1172 break;
1173 }
1174#endif
1175
1177 if (value.length() > descriptor.length)
1178 {
1179 static constexpr std::intptr_t STATUS_STRING_TRUNCATION[] = {
1180 isc_arith_except,
1181 isc_string_truncation,
1182 isc_arg_end,
1183 };
1184
1185 throw DatabaseException(client, STATUS_STRING_TRUNCATION);
1186 }
1187
1188 *reinterpret_cast<std::uint16_t*>(data) = static_cast<std::uint16_t>(value.length());
1189 std::copy(value.begin(), value.end(),
1190 reinterpret_cast<char*>(&message[descriptor.offset + sizeof(std::uint16_t)]));
1191 break;
1192
1193 default:
1194 throwInvalidType("std::string_view", descriptor.adjustedType);
1195 }
1196
1197 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_FALSE;
1198 }
1199
1203 void setBlobId(unsigned index, std::optional<BlobId> optValue)
1204 {
1205 if (!optValue.has_value())
1206 {
1207 setNull(index);
1208 return;
1209 }
1210
1211 assert(isValid());
1212
1213 const auto& value = optValue.value();
1214 const auto& descriptor = getInDescriptor(index);
1215 auto* const message = inMessage.data();
1216
1217 switch (descriptor.adjustedType)
1218 {
1220 *reinterpret_cast<ISC_QUAD*>(&message[descriptor.offset]) = value.id;
1221 break;
1222
1223 default:
1224 throwInvalidType("BlobId", descriptor.adjustedType);
1225 }
1226
1227 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_FALSE;
1228 }
1229
1232 void set(unsigned index, std::nullopt_t)
1233 {
1234 setNull(index);
1235 }
1236
1240 void set(unsigned index, BlobId value)
1241 {
1242 setBlobId(index, value);
1243 }
1244
1248 void set(unsigned index, std::optional<BlobId> value)
1249 {
1250 setBlobId(index, value);
1251 }
1252
1256 void set(unsigned index, bool value)
1257 {
1258 setBool(index, value);
1259 }
1260
1264 void set(unsigned index, std::int16_t value)
1265 {
1266 setInt16(index, value);
1267 }
1268
1272 void set(unsigned index, ScaledInt16 value)
1273 {
1274 setScaledInt16(index, value);
1275 }
1276
1280 void set(unsigned index, std::int32_t value)
1281 {
1282 setInt32(index, value);
1283 }
1284
1288 void set(unsigned index, ScaledInt32 value)
1289 {
1290 setScaledInt32(index, value);
1291 }
1292
1296 void set(unsigned index, std::int64_t value)
1297 {
1298 setInt64(index, value);
1299 }
1300
1304 void set(unsigned index, ScaledInt64 value)
1305 {
1306 setScaledInt64(index, value);
1307 }
1308
1312 void set(unsigned index, OpaqueInt128 value)
1313 {
1314 setOpaqueInt128(index, value);
1315 }
1316
1317#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1321 void set(unsigned index, BoostInt128 value)
1322 {
1323 setBoostInt128(index, value);
1324 }
1325
1329 void set(unsigned index, ScaledBoostInt128 value)
1330 {
1331 setScaledBoostInt128(index, value);
1332 }
1333#endif
1334
1338 void set(unsigned index, float value)
1339 {
1340 setFloat(index, value);
1341 }
1342
1346 void set(unsigned index, double value)
1347 {
1348 setDouble(index, value);
1349 }
1350
1354 void set(unsigned index, OpaqueDecFloat16 value)
1355 {
1356 setOpaqueDecFloat16(index, value);
1357 }
1358
1359#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1363 void set(unsigned index, BoostDecFloat16 value)
1364 {
1365 setBoostDecFloat16(index, value);
1366 }
1367#endif
1368
1372 void set(unsigned index, OpaqueDecFloat34 value)
1373 {
1374 setOpaqueDecFloat34(index, value);
1375 }
1376
1377#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1381 void set(unsigned index, BoostDecFloat34 value)
1382 {
1383 setBoostDecFloat34(index, value);
1384 }
1385#endif
1386
1390 void set(unsigned index, Date value)
1391 {
1392 setDate(index, value);
1393 }
1394
1398 void set(unsigned index, OpaqueDate value)
1399 {
1400 setOpaqueDate(index, value);
1401 }
1402
1406 void set(unsigned index, Time value)
1407 {
1408 setTime(index, value);
1409 }
1410
1414 void set(unsigned index, OpaqueTime value)
1415 {
1416 setOpaqueTime(index, value);
1417 }
1418
1422 void set(unsigned index, Timestamp value)
1423 {
1424 setTimestamp(index, value);
1425 }
1426
1430 void set(unsigned index, OpaqueTimestamp value)
1431 {
1432 setOpaqueTimestamp(index, value);
1433 }
1434
1438 void set(unsigned index, TimeTz value)
1439 {
1440 setTimeTz(index, value);
1441 }
1442
1446 void set(unsigned index, OpaqueTimeTz value)
1447 {
1448 setOpaqueTimeTz(index, value);
1449 }
1450
1454 void set(unsigned index, TimestampTz value)
1455 {
1456 setTimestampTz(index, value);
1457 }
1458
1462 void set(unsigned index, OpaqueTimestampTz value)
1463 {
1464 setOpaqueTimestampTz(index, value);
1465 }
1466
1470 void set(unsigned index, std::string_view value)
1471 {
1472 setString(index, value);
1473 }
1474
1478 template <typename T>
1479 void set(unsigned index, std::optional<T> value)
1480 {
1481 if (value.has_value())
1482 set(index, value.value());
1483 else
1484 setNull(index);
1485 }
1486
1490
1494
1498 bool isNull(unsigned index)
1499 {
1500 assert(isValid());
1501
1502 const auto& descriptor = getOutDescriptor(index);
1503 const auto* const message = outMessage.data();
1504
1505 return *reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE;
1506 }
1507
1511 std::optional<bool> getBool(unsigned index)
1512 {
1513 assert(isValid());
1514
1515 const auto& descriptor = getOutDescriptor(index);
1516 const auto* const message = outMessage.data();
1517
1518 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
1519 return std::nullopt;
1520
1521 switch (descriptor.adjustedType)
1522 {
1524 return message[descriptor.offset] != std::byte{0};
1525
1526 default:
1527 throwInvalidType("bool", descriptor.adjustedType);
1528 }
1529 }
1530
1534 std::optional<std::int16_t> getInt16(unsigned index)
1535 {
1536 std::optional<int> scale{0};
1537 return getNumber<std::int16_t>(index, scale, "std::int16_t");
1538 }
1539
1543 std::optional<ScaledInt16> getScaledInt16(unsigned index)
1544 {
1545 std::optional<int> scale;
1546 const auto value = getNumber<std::int16_t>(index, scale, "ScaledInt16");
1547 return value.has_value() ? std::optional{ScaledInt16{value.value(), scale.value()}} : std::nullopt;
1548 }
1549
1553 std::optional<std::int32_t> getInt32(unsigned index)
1554 {
1555 std::optional<int> scale{0};
1556 return getNumber<std::int32_t>(index, scale, "std::int32_t");
1557 }
1558
1562 std::optional<ScaledInt32> getScaledInt32(unsigned index)
1563 {
1564 std::optional<int> scale;
1565 const auto value = getNumber<std::int32_t>(index, scale, "ScaledInt32");
1566 return value.has_value() ? std::optional{ScaledInt32{value.value(), scale.value()}} : std::nullopt;
1567 }
1568
1572 std::optional<std::int64_t> getInt64(unsigned index)
1573 {
1574 std::optional<int> scale{0};
1575 return getNumber<std::int64_t>(index, scale, "std::int64_t");
1576 }
1577
1581 std::optional<ScaledInt64> getScaledInt64(unsigned index)
1582 {
1583 std::optional<int> scale;
1584 const auto value = getNumber<std::int64_t>(index, scale, "ScaledInt64");
1585 return value.has_value() ? std::optional{ScaledInt64{value.value(), scale.value()}} : std::nullopt;
1586 }
1587
1591 std::optional<ScaledOpaqueInt128> getScaledOpaqueInt128(unsigned index)
1592 {
1593 assert(isValid());
1594
1595 const auto& descriptor = getOutDescriptor(index);
1596 const auto* const message = outMessage.data();
1597
1598 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
1599 return std::nullopt;
1600
1601 switch (descriptor.adjustedType)
1602 {
1604 return ScaledOpaqueInt128{
1605 OpaqueInt128{*reinterpret_cast<const OpaqueInt128*>(&message[descriptor.offset])},
1606 descriptor.scale};
1607
1608 default:
1609 throwInvalidType("ScaledOpaqueInt128", descriptor.adjustedType);
1610 }
1611 }
1612
1613#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1617 std::optional<BoostInt128> getBoostInt128(unsigned index)
1618 {
1619 std::optional<int> scale{0};
1620 const auto value = getNumber<BoostInt128>(index, scale, "BoostInt128");
1621 return value.has_value() ? std::optional{value.value()} : std::nullopt;
1622 }
1623
1627 std::optional<ScaledBoostInt128> getScaledBoostInt128(unsigned index)
1628 {
1629 std::optional<int> scale;
1630 const auto value = getNumber<BoostInt128>(index, scale, "ScaledBoostInt128");
1631 return value.has_value() ? std::optional{ScaledBoostInt128{value.value(), scale.value()}} : std::nullopt;
1632 }
1633#endif
1634
1638 std::optional<float> getFloat(unsigned index)
1639 {
1640 std::optional<int> scale{0};
1641 return getNumber<float>(index, scale, "float");
1642 }
1643
1647 std::optional<double> getDouble(unsigned index)
1648 {
1649 std::optional<int> scale{0};
1650 return getNumber<double>(index, scale, "double");
1651 }
1652
1656 std::optional<OpaqueDecFloat16> getOpaqueDecFloat16(unsigned index)
1657 {
1658 assert(isValid());
1659
1660 const auto& descriptor = getOutDescriptor(index);
1661 const auto* const message = outMessage.data();
1662
1663 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
1664 return std::nullopt;
1665
1666 switch (descriptor.adjustedType)
1667 {
1669 return OpaqueDecFloat16{*reinterpret_cast<const OpaqueDecFloat16*>(&message[descriptor.offset])};
1670
1671 default:
1672 throwInvalidType("OpaqueDecFloat16", descriptor.adjustedType);
1673 }
1674 }
1675
1676#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1680 std::optional<BoostDecFloat16> getBoostDecFloat16(unsigned index)
1681 {
1682 std::optional<int> scale{0};
1683 return getNumber<BoostDecFloat16>(index, scale, "BoostDecFloat16");
1684 }
1685#endif
1686
1690 std::optional<OpaqueDecFloat34> getOpaqueDecFloat34(unsigned index)
1691 {
1692 assert(isValid());
1693
1694 const auto& descriptor = getOutDescriptor(index);
1695 const auto* const message = outMessage.data();
1696
1697 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
1698 return std::nullopt;
1699
1700 switch (descriptor.adjustedType)
1701 {
1703 return OpaqueDecFloat34{*reinterpret_cast<const OpaqueDecFloat34*>(&message[descriptor.offset])};
1704
1705 default:
1706 throwInvalidType("OpaqueDecFloat34", descriptor.adjustedType);
1707 }
1708 }
1709
1710#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1714 std::optional<BoostDecFloat34> getBoostDecFloat34(unsigned index)
1715 {
1716 std::optional<int> scale{0};
1717 return getNumber<BoostDecFloat34>(index, scale, "BoostDecFloat34");
1718 }
1719#endif
1720
1724 std::optional<Date> getDate(unsigned index)
1725 {
1726 assert(isValid());
1727
1728 const auto& descriptor = getOutDescriptor(index);
1729 const auto* const message = outMessage.data();
1730
1731 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
1732 return std::nullopt;
1733
1734 switch (descriptor.adjustedType)
1735 {
1737 return calendarConverter.opaqueDateToDate(
1738 *reinterpret_cast<const OpaqueDate*>(&message[descriptor.offset]));
1739
1740 default:
1741 throwInvalidType("Date", descriptor.adjustedType);
1742 }
1743 }
1744
1748 std::optional<OpaqueDate> getOpaqueDate(unsigned index)
1749 {
1750 assert(isValid());
1751
1752 const auto& descriptor = getOutDescriptor(index);
1753 const auto* const message = outMessage.data();
1754
1755 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
1756 return std::nullopt;
1757
1758 switch (descriptor.adjustedType)
1759 {
1761 return OpaqueDate{*reinterpret_cast<const OpaqueDate*>(&message[descriptor.offset])};
1762
1763 default:
1764 throwInvalidType("OpaqueDate", descriptor.adjustedType);
1765 }
1766 }
1767
1771 std::optional<Time> getTime(unsigned index)
1772 {
1773 assert(isValid());
1774
1775 const auto& descriptor = getOutDescriptor(index);
1776 const auto* const message = outMessage.data();
1777
1778 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
1779 return std::nullopt;
1780
1781 switch (descriptor.adjustedType)
1782 {
1784 return calendarConverter.opaqueTimeToTime(
1785 *reinterpret_cast<const OpaqueTime*>(&message[descriptor.offset]));
1786
1787 default:
1788 throwInvalidType("Time", descriptor.adjustedType);
1789 }
1790 }
1791
1795 std::optional<OpaqueTime> getOpaqueTime(unsigned index)
1796 {
1797 assert(isValid());
1798
1799 const auto& descriptor = getOutDescriptor(index);
1800 const auto* const message = outMessage.data();
1801
1802 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
1803 return std::nullopt;
1804
1805 switch (descriptor.adjustedType)
1806 {
1808 return OpaqueTime{*reinterpret_cast<const OpaqueTime*>(&message[descriptor.offset])};
1809
1810 default:
1811 throwInvalidType("OpaqueTime", descriptor.adjustedType);
1812 }
1813 }
1814
1818 std::optional<Timestamp> getTimestamp(unsigned index)
1819 {
1820 assert(isValid());
1821
1822 const auto& descriptor = getOutDescriptor(index);
1823 const auto* const message = outMessage.data();
1824
1825 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
1826 return std::nullopt;
1827
1828 switch (descriptor.adjustedType)
1829 {
1831 return calendarConverter.opaqueTimestampToTimestamp(
1832 *reinterpret_cast<const OpaqueTimestamp*>(&message[descriptor.offset]));
1833
1834 default:
1835 throwInvalidType("Timestamp", descriptor.adjustedType);
1836 }
1837 }
1838
1842 std::optional<OpaqueTimestamp> getOpaqueTimestamp(unsigned index)
1843 {
1844 assert(isValid());
1845
1846 const auto& descriptor = getOutDescriptor(index);
1847 const auto* const message = outMessage.data();
1848
1849 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
1850 return std::nullopt;
1851
1852 switch (descriptor.adjustedType)
1853 {
1855 return OpaqueTimestamp{*reinterpret_cast<const OpaqueTimestamp*>(&message[descriptor.offset])};
1856
1857 default:
1858 throwInvalidType("OpaqueTimestamp", descriptor.adjustedType);
1859 }
1860 }
1861
1865 std::optional<TimeTz> getTimeTz(unsigned index)
1866 {
1867 assert(isValid());
1868
1869 const auto& descriptor = getOutDescriptor(index);
1870 const auto* const message = outMessage.data();
1871
1872 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
1873 return std::nullopt;
1874
1875 switch (descriptor.adjustedType)
1876 {
1878 return calendarConverter.opaqueTimeTzToTimeTz(
1879 *reinterpret_cast<const OpaqueTimeTz*>(&message[descriptor.offset]));
1880
1881 default:
1882 throwInvalidType("TimeTz", descriptor.adjustedType);
1883 }
1884 }
1885
1889 std::optional<OpaqueTimeTz> getOpaqueTimeTz(unsigned index)
1890 {
1891 assert(isValid());
1892
1893 const auto& descriptor = getOutDescriptor(index);
1894 const auto* const message = outMessage.data();
1895
1896 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
1897 return std::nullopt;
1898
1899 switch (descriptor.adjustedType)
1900 {
1902 return OpaqueTimeTz{*reinterpret_cast<const OpaqueTimeTz*>(&message[descriptor.offset])};
1903
1904 default:
1905 throwInvalidType("OpaqueTimeTz", descriptor.adjustedType);
1906 }
1907 }
1908
1912 std::optional<TimestampTz> getTimestampTz(unsigned index)
1913 {
1914 assert(isValid());
1915
1916 const auto& descriptor = getOutDescriptor(index);
1917 const auto* const message = outMessage.data();
1918
1919 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
1920 return std::nullopt;
1921
1922 switch (descriptor.adjustedType)
1923 {
1925 return calendarConverter.opaqueTimestampTzToTimestampTz(
1926 *reinterpret_cast<const OpaqueTimestampTz*>(&message[descriptor.offset]));
1927
1928 default:
1929 throwInvalidType("TimestampTz", descriptor.adjustedType);
1930 }
1931 }
1932
1936 std::optional<OpaqueTimestampTz> getOpaqueTimestampTz(unsigned index)
1937 {
1938 assert(isValid());
1939
1940 const auto& descriptor = getOutDescriptor(index);
1941 const auto* const message = outMessage.data();
1942
1943 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
1944 return std::nullopt;
1945
1946 switch (descriptor.adjustedType)
1947 {
1949 return OpaqueTimestampTz{*reinterpret_cast<const OpaqueTimestampTz*>(&message[descriptor.offset])};
1950
1951 default:
1952 throwInvalidType("OpaqueTimestampTz", descriptor.adjustedType);
1953 }
1954 }
1955
1959 std::optional<BlobId> getBlobId(unsigned index)
1960 {
1961 assert(isValid());
1962
1963 const auto& descriptor = getOutDescriptor(index);
1964 const auto* const message = outMessage.data();
1965
1966 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
1967 return std::nullopt;
1968
1969 switch (descriptor.adjustedType)
1970 {
1972 {
1973 BlobId value;
1974 value.id = *reinterpret_cast<const ISC_QUAD*>(&message[descriptor.offset]);
1975 return value;
1976 }
1977
1978 default:
1979 throwInvalidType("BlobId", descriptor.adjustedType);
1980 }
1981 }
1982
1986 std::optional<std::string> getString(unsigned index)
1987 {
1988 assert(isValid());
1989
1990 const auto& descriptor = getOutDescriptor(index);
1991 const auto* const message = outMessage.data();
1992
1993 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
1994 return std::nullopt;
1995
1996 const auto data = &message[descriptor.offset];
1997
1998 switch (descriptor.adjustedType)
1999 {
2001 return (message[descriptor.offset] != std::byte{0}) ? std::string{"true"} : std::string{"false"};
2002
2004 return numericConverter.numberToString(
2005 ScaledInt16{*reinterpret_cast<const std::int16_t*>(data), descriptor.scale});
2006
2008 return numericConverter.numberToString(
2009 ScaledInt32{*reinterpret_cast<const std::int32_t*>(data), descriptor.scale});
2010
2012 return numericConverter.numberToString(
2013 ScaledInt64{*reinterpret_cast<const std::int64_t*>(data), descriptor.scale});
2014
2016 return numericConverter.opaqueInt128ToString(
2017 *reinterpret_cast<const OpaqueInt128*>(data), descriptor.scale);
2018
2020 return numericConverter.numberToString(*reinterpret_cast<const float*>(data));
2021
2023 return numericConverter.numberToString(*reinterpret_cast<const double*>(data));
2024
2026 return calendarConverter.opaqueDateToString(*reinterpret_cast<const OpaqueDate*>(data));
2027
2029 return calendarConverter.opaqueTimeToString(*reinterpret_cast<const OpaqueTime*>(data));
2030
2032 return calendarConverter.opaqueTimestampToString(*reinterpret_cast<const OpaqueTimestamp*>(data));
2033
2035 return calendarConverter.opaqueTimeTzToString(*reinterpret_cast<const OpaqueTimeTz*>(data));
2036
2038 return calendarConverter.opaqueTimestampTzToString(
2039 *reinterpret_cast<const OpaqueTimestampTz*>(data));
2040
2042 return numericConverter.opaqueDecFloat16ToString(*reinterpret_cast<const OpaqueDecFloat16*>(data));
2043
2045 return numericConverter.opaqueDecFloat34ToString(*reinterpret_cast<const OpaqueDecFloat34*>(data));
2046
2048 return std::string{reinterpret_cast<const char*>(data + sizeof(std::uint16_t)),
2049 *reinterpret_cast<const std::uint16_t*>(data)};
2050
2051 default:
2052 throwInvalidType("std::string", descriptor.adjustedType);
2053 }
2054 }
2055
2059
2063 template <typename T>
2064 T get(unsigned index);
2065
2073 template <Aggregate T>
2075 {
2076 using namespace impl::reflection;
2077
2078 constexpr std::size_t N = fieldCountV<T>;
2079
2080 if (N != outDescriptors.size())
2081 {
2082 throw FbCppException("Struct field count (" + std::to_string(N) +
2083 ") does not match output column count (" + std::to_string(outDescriptors.size()) + ")");
2084 }
2085
2086 return getStruct<T>(std::make_index_sequence<N>{});
2087 }
2088
2095 template <Aggregate T>
2096 void set(const T& value)
2097 {
2098 using namespace impl::reflection;
2099
2100 constexpr std::size_t N = fieldCountV<T>;
2101
2102 if (N != inDescriptors.size())
2103 {
2104 throw FbCppException("Struct field count (" + std::to_string(N) +
2105 ") does not match input parameter count (" + std::to_string(inDescriptors.size()) + ")");
2106 }
2107
2108 setStruct(value, std::make_index_sequence<N>{});
2109 }
2110
2118 template <TupleLike T>
2120 {
2121 using namespace impl::reflection;
2122
2123 constexpr std::size_t N = std::tuple_size_v<T>;
2124
2125 if (N != outDescriptors.size())
2126 {
2127 throw FbCppException("Tuple element count (" + std::to_string(N) +
2128 ") does not match output column count (" + std::to_string(outDescriptors.size()) + ")");
2129 }
2130
2131 return getTuple<T>(std::make_index_sequence<N>{});
2132 }
2133
2140 template <TupleLike T>
2141 void set(const T& value)
2142 {
2143 constexpr std::size_t N = std::tuple_size_v<T>;
2144
2145 if (N != inDescriptors.size())
2146 {
2147 throw FbCppException("Tuple element count (" + std::to_string(N) +
2148 ") does not match input parameter count (" + std::to_string(inDescriptors.size()) + ")");
2149 }
2150
2151 setTuple(value, std::make_index_sequence<N>{});
2152 }
2153
2162 template <VariantLike V>
2163 V get(unsigned index)
2164 {
2165 using namespace impl::reflection;
2166
2167 static_assert(variantAlternativesSupportedV<V>,
2168 "Variant contains unsupported types. All variant alternatives must be types supported by fb-cpp "
2169 "(e.g., std::int32_t, std::string, Date, ScaledOpaqueInt128, etc.). Check VariantTypeTraits.h for the "
2170 "complete list of supported types.");
2171
2172 assert(isValid());
2173
2174 const auto& descriptor = getOutDescriptor(index);
2175
2176 if (isNull(index))
2177 {
2178 if constexpr (variantContainsV<std::monostate, V>)
2179 return V{std::monostate{}};
2180 else
2181 {
2182 throw FbCppException(
2183 "NULL value encountered but variant does not contain std::monostate at index " +
2184 std::to_string(index));
2185 }
2186 }
2187
2188 return getVariantValue<V>(index, descriptor);
2189 }
2190
2197 template <VariantLike V>
2198 void set(unsigned index, const V& value)
2199 {
2200 using namespace impl::reflection;
2201
2202 static_assert(variantAlternativesSupportedV<V>,
2203 "Variant contains unsupported types. All variant alternatives must be types supported by fb-cpp "
2204 "(e.g., std::int32_t, std::string, Date, ScaledOpaqueInt128, etc.). Check VariantTypeTraits.h for the "
2205 "complete list of supported types.");
2206
2207 std::visit(
2208 [this, index](const auto& v)
2209 {
2210 using T = std::decay_t<decltype(v)>;
2211
2212 if constexpr (std::is_same_v<T, std::monostate>)
2213 setNull(index);
2214 else
2215 set(index, v);
2216 },
2217 value);
2218 }
2219
2220 private:
2224 const Descriptor& getInDescriptor(unsigned index)
2225 {
2226 if (index >= inDescriptors.size())
2227 throw std::out_of_range("index out of range");
2228
2229 return inDescriptors[index];
2230 }
2231
2235 const Descriptor& getOutDescriptor(unsigned index)
2236 {
2237 if (index >= outDescriptors.size())
2238 throw std::out_of_range("index out of range");
2239
2240 return outDescriptors[index];
2241 }
2242
2246 template <typename T, std::size_t... Is>
2247 T getStruct(std::index_sequence<Is...>)
2248 {
2249 using namespace impl::reflection;
2250
2251 return T{getStructField<FieldType<T, Is>>(static_cast<unsigned>(Is))...};
2252 }
2253
2257 template <typename F>
2258 auto getStructField(unsigned index)
2259 {
2260 using namespace impl::reflection;
2261
2262 if constexpr (isOptionalV<F>)
2263 return get<F>(index);
2264 else if constexpr (isVariantV<F>)
2265 return get<F>(index);
2266 else
2267 {
2268 auto opt = get<std::optional<F>>(index);
2269
2270 if (!opt.has_value())
2271 {
2272 throw FbCppException(
2273 "Null value encountered for non-optional field at index " + std::to_string(index));
2274 }
2275
2276 return std::move(opt.value());
2277 }
2278 }
2279
2283 template <typename T, std::size_t... Is>
2284 void setStruct(const T& value, std::index_sequence<Is...>)
2285 {
2286 using namespace impl::reflection;
2287
2288 const auto tuple = toTupleRef(value);
2289 (set(static_cast<unsigned>(Is), std::get<Is>(tuple)), ...);
2290 }
2291
2295 template <typename T, std::size_t... Is>
2296 T getTuple(std::index_sequence<Is...>)
2297 {
2298 using namespace impl::reflection;
2299
2300 return T{getStructField<std::tuple_element_t<Is, T>>(static_cast<unsigned>(Is))...};
2301 }
2302
2306 template <typename T, std::size_t... Is>
2307 void setTuple(const T& value, std::index_sequence<Is...>)
2308 {
2309 (set(static_cast<unsigned>(Is), std::get<Is>(value)), ...);
2310 }
2311
2316 template <typename V>
2317 V getVariantValue(unsigned index, const Descriptor& descriptor)
2318 {
2319 using namespace impl::reflection;
2320
2321 // Try exact type matches first based on SQL type
2322 switch (descriptor.adjustedType)
2323 {
2325 if constexpr (variantContainsV<bool, V>)
2326 return V{get<std::optional<bool>>(index).value()};
2327 break;
2328
2330 if (descriptor.scale != 0)
2331 {
2332 // For scaled numbers, prefer exact scaled type, then larger scaled types
2333 if constexpr (variantContainsV<ScaledInt16, V>)
2334 return V{get<std::optional<ScaledInt16>>(index).value()};
2335 if constexpr (variantContainsV<ScaledInt32, V>)
2336 return V{get<std::optional<ScaledInt32>>(index).value()};
2337 if constexpr (variantContainsV<ScaledInt64, V>)
2338 return V{get<std::optional<ScaledInt64>>(index).value()};
2339#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
2340 if constexpr (variantContainsV<ScaledBoostInt128, V>)
2341 return V{get<std::optional<ScaledBoostInt128>>(index).value()};
2342#endif
2343 }
2344 if constexpr (variantContainsV<std::int16_t, V>)
2345 return V{get<std::optional<std::int16_t>>(index).value()};
2346 break;
2347
2349 if (descriptor.scale != 0)
2350 {
2351 // For scaled numbers, prefer exact scaled type, then larger scaled types
2352 if constexpr (variantContainsV<ScaledInt32, V>)
2353 return V{get<std::optional<ScaledInt32>>(index).value()};
2354 if constexpr (variantContainsV<ScaledInt64, V>)
2355 return V{get<std::optional<ScaledInt64>>(index).value()};
2356#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
2357 if constexpr (variantContainsV<ScaledBoostInt128, V>)
2358 return V{get<std::optional<ScaledBoostInt128>>(index).value()};
2359#endif
2360 }
2361 if constexpr (variantContainsV<std::int32_t, V>)
2362 return V{get<std::optional<std::int32_t>>(index).value()};
2363 break;
2364
2366 if (descriptor.scale != 0)
2367 {
2368 // For scaled numbers, prefer exact scaled type, then larger scaled types
2369 if constexpr (variantContainsV<ScaledInt64, V>)
2370 return V{get<std::optional<ScaledInt64>>(index).value()};
2371#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
2372 if constexpr (variantContainsV<ScaledBoostInt128, V>)
2373 return V{get<std::optional<ScaledBoostInt128>>(index).value()};
2374#endif
2375 }
2376 if constexpr (variantContainsV<std::int64_t, V>)
2377 return V{get<std::optional<std::int64_t>>(index).value()};
2378 break;
2379
2380#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
2382 // Prefer opaque (native Firebird) types first
2383 if constexpr (variantContainsV<ScaledOpaqueInt128, V>)
2384 return V{get<std::optional<ScaledOpaqueInt128>>(index).value()};
2385 else if (descriptor.scale != 0)
2386 {
2387 if constexpr (variantContainsV<ScaledBoostInt128, V>)
2388 return V{get<std::optional<ScaledBoostInt128>>(index).value()};
2389 }
2390 else if constexpr (variantContainsV<BoostInt128, V>)
2391 return V{get<std::optional<BoostInt128>>(index).value()};
2392 break;
2393#endif
2394
2396 if constexpr (variantContainsV<float, V>)
2397 return V{get<std::optional<float>>(index).value()};
2398 break;
2399
2401 if constexpr (variantContainsV<double, V>)
2402 return V{get<std::optional<double>>(index).value()};
2403 break;
2404
2405#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
2407 // Prefer opaque (native Firebird) types first
2408 if constexpr (variantContainsV<OpaqueDecFloat16, V>)
2409 return V{get<std::optional<OpaqueDecFloat16>>(index).value()};
2410 else if constexpr (variantContainsV<BoostDecFloat16, V>)
2411 return V{get<std::optional<BoostDecFloat16>>(index).value()};
2412 break;
2413
2415 // Prefer opaque (native Firebird) types first
2416 if constexpr (variantContainsV<OpaqueDecFloat34, V>)
2417 return V{get<std::optional<OpaqueDecFloat34>>(index).value()};
2418 else if constexpr (variantContainsV<BoostDecFloat34, V>)
2419 return V{get<std::optional<BoostDecFloat34>>(index).value()};
2420 break;
2421#endif
2422
2424 if constexpr (variantContainsV<std::string, V>)
2425 return V{get<std::optional<std::string>>(index).value()};
2426 break;
2427
2429 // Prefer opaque (native Firebird) types first
2430 if constexpr (variantContainsV<OpaqueDate, V>)
2431 return V{get<std::optional<OpaqueDate>>(index).value()};
2432 else if constexpr (variantContainsV<Date, V>)
2433 return V{get<std::optional<Date>>(index).value()};
2434 break;
2435
2437 // Prefer opaque (native Firebird) types first
2438 if constexpr (variantContainsV<OpaqueTime, V>)
2439 return V{get<std::optional<OpaqueTime>>(index).value()};
2440 else if constexpr (variantContainsV<Time, V>)
2441 return V{get<std::optional<Time>>(index).value()};
2442 break;
2443
2445 // Prefer opaque (native Firebird) types first
2446 if constexpr (variantContainsV<OpaqueTimestamp, V>)
2447 return V{get<std::optional<OpaqueTimestamp>>(index).value()};
2448 else if constexpr (variantContainsV<Timestamp, V>)
2449 return V{get<std::optional<Timestamp>>(index).value()};
2450 break;
2451
2453 // Prefer opaque (native Firebird) types first
2454 if constexpr (variantContainsV<OpaqueTimeTz, V>)
2455 return V{get<std::optional<OpaqueTimeTz>>(index).value()};
2456 else if constexpr (variantContainsV<TimeTz, V>)
2457 return V{get<std::optional<TimeTz>>(index).value()};
2458 break;
2459
2461 // Prefer opaque (native Firebird) types first
2462 if constexpr (variantContainsV<OpaqueTimestampTz, V>)
2463 return V{get<std::optional<OpaqueTimestampTz>>(index).value()};
2464 else if constexpr (variantContainsV<TimestampTz, V>)
2465 return V{get<std::optional<TimestampTz>>(index).value()};
2466 break;
2467
2469 if constexpr (variantContainsV<BlobId, V>)
2470 return V{get<std::optional<BlobId>>(index).value()};
2471 break;
2472
2473 default:
2474 break;
2475 }
2476
2477 // No exact match found, try variant alternatives in declaration order
2478 return tryVariantAlternatives<V, 0>(index, descriptor);
2479 }
2480
2484 template <typename V, std::size_t I = 0>
2485 V tryVariantAlternatives(unsigned index, [[maybe_unused]] const Descriptor& descriptor)
2486 {
2487 using namespace impl::reflection;
2488
2489 if constexpr (I >= std::variant_size_v<V>)
2490 {
2491 throw FbCppException(
2492 "Cannot convert SQL type to any variant alternative at index " + std::to_string(index));
2493 }
2494 else
2495 {
2496 using Alt = std::variant_alternative_t<I, V>;
2497
2498 if constexpr (std::is_same_v<Alt, std::monostate>)
2499 {
2500 // Skip monostate in non-null case
2501 return tryVariantAlternatives<V, I + 1>(index, descriptor);
2502 }
2503 else if constexpr (isOpaqueTypeV<Alt>)
2504 {
2505 // Skip opaque types - they only match exact SQL types, no conversions
2506 return tryVariantAlternatives<V, I + 1>(index, descriptor);
2507 }
2508 else
2509 {
2510 // Try this alternative - get<T> will throw if conversion fails
2511 auto opt = get<std::optional<Alt>>(index);
2512 return V{std::move(opt.value())};
2513 }
2514 }
2515 }
2516
2520 template <typename T>
2521 void setNumber(unsigned index, DescriptorAdjustedType valueType, T value, int scale, const char* typeName)
2522 {
2523 assert(isValid());
2524
2525 const auto& descriptor = getInDescriptor(index);
2526 auto* const message = inMessage.data();
2527
2528 const auto descriptorData = &message[descriptor.offset];
2529 std::optional<int> descriptorScale{descriptor.scale};
2530
2531 Descriptor valueDescriptor;
2532 valueDescriptor.adjustedType = valueType;
2533 valueDescriptor.scale = scale;
2534
2535 const auto valueAddress = reinterpret_cast<const std::byte*>(&value);
2536
2537 switch (descriptor.adjustedType)
2538 {
2540 *reinterpret_cast<std::int16_t*>(descriptorData) =
2541 convertNumber<std::int16_t>(valueDescriptor, valueAddress, descriptorScale, "std::int16_t");
2542 break;
2543
2545 *reinterpret_cast<std::int32_t*>(descriptorData) =
2546 convertNumber<std::int32_t>(valueDescriptor, valueAddress, descriptorScale, "std::int32_t");
2547 break;
2548
2550 *reinterpret_cast<std::int64_t*>(descriptorData) =
2551 convertNumber<std::int64_t>(valueDescriptor, valueAddress, descriptorScale, "std::int64_t");
2552 break;
2553
2554#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
2556 {
2557 const auto boostInt128 =
2558 convertNumber<BoostInt128>(valueDescriptor, valueAddress, descriptorScale, "BoostInt128");
2559 *reinterpret_cast<OpaqueInt128*>(descriptorData) =
2560 numericConverter.boostInt128ToOpaqueInt128(boostInt128);
2561 break;
2562 }
2563#endif
2564
2566 *reinterpret_cast<float*>(descriptorData) =
2567 convertNumber<float>(valueDescriptor, valueAddress, descriptorScale, "float");
2568 break;
2569
2571 *reinterpret_cast<double*>(descriptorData) =
2572 convertNumber<double>(valueDescriptor, valueAddress, descriptorScale, "double");
2573 break;
2574
2575#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
2577 {
2578 const auto boostDecFloat16 = convertNumber<BoostDecFloat16>(
2579 valueDescriptor, valueAddress, descriptorScale, "BoostDecFloat16");
2580 *reinterpret_cast<OpaqueDecFloat16*>(descriptorData) =
2581 numericConverter.boostDecFloat16ToOpaqueDecFloat16(boostDecFloat16);
2582 break;
2583 }
2584
2586 {
2587 const auto boostDecFloat34 = convertNumber<BoostDecFloat34>(
2588 valueDescriptor, valueAddress, descriptorScale, "BoostDecFloat34");
2589 *reinterpret_cast<OpaqueDecFloat34*>(descriptorData) =
2590 numericConverter.boostDecFloat34ToOpaqueDecFloat34(boostDecFloat34);
2591 break;
2592 }
2593#endif
2594
2595 default:
2596 throwInvalidType(typeName, descriptor.adjustedType);
2597 }
2598
2599 *reinterpret_cast<std::int16_t*>(&message[descriptor.nullOffset]) = FB_FALSE;
2600 }
2601
2602 // FIXME: floating to integral
2606 template <typename T>
2607 std::optional<T> getNumber(unsigned index, std::optional<int>& scale, const char* typeName)
2608 {
2609 assert(isValid());
2610
2611 const auto& descriptor = getOutDescriptor(index);
2612 const auto* const message = outMessage.data();
2613
2614 if (*reinterpret_cast<const std::int16_t*>(&message[descriptor.nullOffset]) != FB_FALSE)
2615 return std::nullopt;
2616
2617 auto data = &message[descriptor.offset];
2618#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
2619 std::optional<BoostInt128> boostInt128;
2620 std::optional<BoostDecFloat16> boostDecFloat16;
2621 std::optional<BoostDecFloat34> boostDecFloat34;
2622#endif
2623
2624 // FIXME: Use IUtil
2625 switch (descriptor.adjustedType)
2626 {
2627#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
2629 boostInt128.emplace(
2630 numericConverter.opaqueInt128ToBoostInt128(*reinterpret_cast<const OpaqueInt128*>(data)));
2631 data = reinterpret_cast<const std::byte*>(&boostInt128.value());
2632 break;
2633
2635 boostDecFloat16.emplace(numericConverter.opaqueDecFloat16ToBoostDecFloat16(
2636 *reinterpret_cast<const OpaqueDecFloat16*>(data)));
2637 data = reinterpret_cast<const std::byte*>(&boostDecFloat16.value());
2638 break;
2639
2641 boostDecFloat34.emplace(numericConverter.opaqueDecFloat34ToBoostDecFloat34(
2642 *reinterpret_cast<const OpaqueDecFloat34*>(data)));
2643 data = reinterpret_cast<const std::byte*>(&boostDecFloat34.value());
2644 break;
2645#endif
2646
2647 default:
2648 break;
2649 }
2650
2651 return convertNumber<T>(descriptor, data, scale, typeName);
2652 }
2653
2654 [[noreturn]] static void throwInvalidType(const char* actualType, DescriptorAdjustedType descriptorType)
2655 {
2656 throw FbCppException("Invalid type: actual type " + std::string(actualType) + ", descriptor type " +
2657 std::to_string(static_cast<unsigned>(descriptorType)));
2658 }
2659
2660 template <typename T>
2661 T convertNumber(
2662 const Descriptor& descriptor, const std::byte* data, std::optional<int>& toScale, const char* toTypeName)
2663 {
2664 if (!toScale.has_value())
2665 {
2666 switch (descriptor.adjustedType)
2667 {
2672 throwInvalidType(toTypeName, descriptor.adjustedType);
2673
2674 default:
2675 break;
2676 }
2677
2678 toScale = descriptor.scale;
2679 }
2680
2681 switch (descriptor.adjustedType)
2682 {
2684 return numericConverter.numberToNumber<T>(
2685 ScaledInt16{*reinterpret_cast<const std::int16_t*>(data), descriptor.scale}, toScale.value());
2686 break;
2687
2689 return numericConverter.numberToNumber<T>(
2690 ScaledInt32{*reinterpret_cast<const std::int32_t*>(data), descriptor.scale}, toScale.value());
2691
2693 return numericConverter.numberToNumber<T>(
2694 ScaledInt64{*reinterpret_cast<const std::int64_t*>(data), descriptor.scale}, toScale.value());
2695
2696#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
2698 return numericConverter.numberToNumber<T>(
2699 ScaledBoostInt128{*reinterpret_cast<const BoostInt128*>(data), descriptor.scale},
2700 toScale.value());
2701
2703 return numericConverter.numberToNumber<T>(
2704 *reinterpret_cast<const BoostDecFloat16*>(data), toScale.value());
2705
2707 return numericConverter.numberToNumber<T>(
2708 *reinterpret_cast<const BoostDecFloat34*>(data), toScale.value());
2709#endif
2710
2712 return numericConverter.numberToNumber<T>(*reinterpret_cast<const float*>(data), toScale.value());
2713 break;
2714
2716 return numericConverter.numberToNumber<T>(*reinterpret_cast<const double*>(data), toScale.value());
2717 break;
2718
2719 default:
2720 throwInvalidType(toTypeName, descriptor.adjustedType);
2721 }
2722 }
2723
2724 private:
2725 Attachment& attachment;
2726 FbUniquePtr<Firebird::IStatus> status;
2727 impl::StatusWrapper statusWrapper;
2728 impl::CalendarConverter calendarConverter;
2729 impl::NumericConverter numericConverter;
2730 FbRef<fb::IStatement> statementHandle;
2731 FbRef<fb::IResultSet> resultSetHandle;
2732 FbRef<fb::IMessageMetadata> inMetadata;
2733 std::vector<Descriptor> inDescriptors;
2734 std::vector<std::byte> inMessage;
2735 FbRef<fb::IMessageMetadata> outMetadata;
2736 std::vector<Descriptor> outDescriptors;
2737 std::vector<std::byte> outMessage;
2738 StatementType type;
2739 };
2740
2745
2746 template <>
2747 inline std::optional<bool> Statement::get<std::optional<bool>>(unsigned index)
2748 {
2749 return getBool(index);
2750 }
2751
2752 template <>
2753 inline std::optional<BlobId> Statement::get<std::optional<BlobId>>(unsigned index)
2754 {
2755 return getBlobId(index);
2756 }
2757
2758 template <>
2759 inline std::optional<std::int16_t> Statement::get<std::optional<std::int16_t>>(unsigned index)
2760 {
2761 return getInt16(index);
2762 }
2763
2764 template <>
2765 inline std::optional<ScaledInt16> Statement::get<std::optional<ScaledInt16>>(unsigned index)
2766 {
2767 return getScaledInt16(index);
2768 }
2769
2770 template <>
2771 inline std::optional<std::int32_t> Statement::get<std::optional<std::int32_t>>(unsigned index)
2772 {
2773 return getInt32(index);
2774 }
2775
2776 template <>
2777 inline std::optional<ScaledInt32> Statement::get<std::optional<ScaledInt32>>(unsigned index)
2778 {
2779 return getScaledInt32(index);
2780 }
2781
2782 template <>
2783 inline std::optional<std::int64_t> Statement::get<std::optional<std::int64_t>>(unsigned index)
2784 {
2785 return getInt64(index);
2786 }
2787
2788 template <>
2789 inline std::optional<ScaledInt64> Statement::get<std::optional<ScaledInt64>>(unsigned index)
2790 {
2791 return getScaledInt64(index);
2792 }
2793
2794 template <>
2795 inline std::optional<ScaledOpaqueInt128> Statement::get<std::optional<ScaledOpaqueInt128>>(unsigned index)
2796 {
2797 return getScaledOpaqueInt128(index);
2798 }
2799
2800#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
2801 template <>
2802 inline std::optional<BoostInt128> Statement::get<std::optional<BoostInt128>>(unsigned index)
2803 {
2804 return getBoostInt128(index);
2805 }
2806
2807 template <>
2808 inline std::optional<ScaledBoostInt128> Statement::get<std::optional<ScaledBoostInt128>>(unsigned index)
2809 {
2810 return getScaledBoostInt128(index);
2811 }
2812#endif
2813
2814 template <>
2815 inline std::optional<float> Statement::get<std::optional<float>>(unsigned index)
2816 {
2817 return getFloat(index);
2818 }
2819
2820 template <>
2821 inline std::optional<double> Statement::get<std::optional<double>>(unsigned index)
2822 {
2823 return getDouble(index);
2824 }
2825
2826 template <>
2827 inline std::optional<OpaqueDecFloat16> Statement::get<std::optional<OpaqueDecFloat16>>(unsigned index)
2828 {
2829 return getOpaqueDecFloat16(index);
2830 }
2831
2832#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
2833 template <>
2834 inline std::optional<BoostDecFloat16> Statement::get<std::optional<BoostDecFloat16>>(unsigned index)
2835 {
2836 return getBoostDecFloat16(index);
2837 }
2838#endif
2839
2840 template <>
2841 inline std::optional<OpaqueDecFloat34> Statement::get<std::optional<OpaqueDecFloat34>>(unsigned index)
2842 {
2843 return getOpaqueDecFloat34(index);
2844 }
2845
2846#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
2847 template <>
2848 inline std::optional<BoostDecFloat34> Statement::get<std::optional<BoostDecFloat34>>(unsigned index)
2849 {
2850 return getBoostDecFloat34(index);
2851 }
2852#endif
2853
2854 template <>
2855 inline std::optional<Date> Statement::get<std::optional<Date>>(unsigned index)
2856 {
2857 return getDate(index);
2858 }
2859
2860 template <>
2861 inline std::optional<OpaqueDate> Statement::get<std::optional<OpaqueDate>>(unsigned index)
2862 {
2863 return getOpaqueDate(index);
2864 }
2865
2866 template <>
2867 inline std::optional<Time> Statement::get<std::optional<Time>>(unsigned index)
2868 {
2869 return getTime(index);
2870 }
2871
2872 template <>
2873 inline std::optional<OpaqueTime> Statement::get<std::optional<OpaqueTime>>(unsigned index)
2874 {
2875 return getOpaqueTime(index);
2876 }
2877
2878 template <>
2879 inline std::optional<OpaqueTimestamp> Statement::get<std::optional<OpaqueTimestamp>>(unsigned index)
2880 {
2881 return getOpaqueTimestamp(index);
2882 }
2883
2884 template <>
2885 inline std::optional<Timestamp> Statement::get<std::optional<Timestamp>>(unsigned index)
2886 {
2887 return getTimestamp(index);
2888 }
2889
2890 template <>
2891 inline std::optional<TimeTz> Statement::get<std::optional<TimeTz>>(unsigned index)
2892 {
2893 return getTimeTz(index);
2894 }
2895
2896 template <>
2897 inline std::optional<OpaqueTimeTz> Statement::get<std::optional<OpaqueTimeTz>>(unsigned index)
2898 {
2899 return getOpaqueTimeTz(index);
2900 }
2901
2902 template <>
2903 inline std::optional<TimestampTz> Statement::get<std::optional<TimestampTz>>(unsigned index)
2904 {
2905 return getTimestampTz(index);
2906 }
2907
2908 template <>
2909 inline std::optional<OpaqueTimestampTz> Statement::get<std::optional<OpaqueTimestampTz>>(unsigned index)
2910 {
2911 return getOpaqueTimestampTz(index);
2912 }
2913
2914 template <>
2915 inline std::optional<std::string> Statement::get<std::optional<std::string>>(unsigned index)
2916 {
2917 return getString(index);
2918 }
2919
2923} // namespace fbcpp
2924
2925
2926#endif // FBCPP_STATEMENT_H
Represents a connection to a Firebird database.
Definition Attachment.h:177
Client & getClient() noexcept
Returns the Client object reference used to create this Attachment object.
Definition Attachment.h:230
Represents a Firebird blob identifier.
Definition Blob.h:52
ISC_QUAD id
Stores the raw Firebird blob identifier value.
Definition Blob.h:64
Exception thrown when a Firebird database operation fails.
Definition Exception.h:200
Base exception class for all fb-cpp exceptions.
Definition Exception.h:183
Reference-counted smart pointer for Firebird objects using addRef/release semantics.
Definition SmartPtrs.h:70
Represents options used when preparing a Statement.
Definition Statement.h:73
const std::optional< std::string > & getCursorName() const
Returns the cursor name to be set for the statement.
Definition Statement.h:116
StatementOptions & setPrefetchLegacyPlan(bool value)
Enables or disables prefetching of the legacy textual plan at prepare time.
Definition Statement.h:88
bool getPrefetchPlan() const
Reports whether the structured plan should be prefetched during prepare.
Definition Statement.h:97
StatementOptions & setCursorName(const std::string &value)
Sets the cursor name for the statement.
Definition Statement.h:126
StatementOptions & setPrefetchPlan(bool value)
Enables or disables prefetching of the structured plan at prepare time.
Definition Statement.h:107
bool getPrefetchLegacyPlan() const
Reports whether the legacy textual plan should be prefetched during prepare.
Definition Statement.h:78
Prepares, executes, and fetches SQL statements against a Firebird attachment.
Definition Statement.h:205
void set(unsigned index, OpaqueInt128 value)
Convenience overload that binds a Firebird 128-bit integer.
Definition Statement.h:1312
void setScaledInt16(unsigned index, std::optional< ScaledInt16 > optValue)
Binds a scaled 16-bit signed integer value or null.
Definition Statement.h:487
V get(unsigned index)
Retrieves a column value as a user-defined variant type.
Definition Statement.h:2163
bool fetchAbsolute(unsigned position)
Positions the cursor on the given absolute row number.
void setNull(unsigned index)
Marks the specified parameter as null.
Definition Statement.h:428
bool fetchRelative(int offset)
Moves the cursor by the requested relative offset.
void setScaledBoostInt128(unsigned index, std::optional< ScaledBoostInt128 > optValue)
Binds a scaled 128-bit integer value expressed with Boost.Multiprecision or null.
Definition Statement.h:605
std::optional< OpaqueTime > getOpaqueTime(unsigned index)
Reads a raw time-of-day column in Firebird's representation.
Definition Statement.h:1795
std::optional< OpaqueDecFloat34 > getOpaqueDecFloat34(unsigned index)
Reads a Firebird 34-digit decimal floating-point column.
Definition Statement.h:1690
void setDate(unsigned index, std::optional< Date > optValue)
Binds a date value or null.
Definition Statement.h:741
std::string getLegacyPlan()
Retrieves the textual legacy plan if the server produced one.
const std::vector< Descriptor > & getInputDescriptors() noexcept
Provides cached descriptors for each input column.
Definition Statement.h:330
void free()
Releases the prepared handle and any associated result set.
const std::vector< Descriptor > & getOutputDescriptors() noexcept
Provides cached descriptors for each output column.
Definition Statement.h:338
bool isValid() noexcept
Returns whether the Statement object is valid.
Definition Statement.h:270
StatementType getType() noexcept
Returns the type classification reported by the server.
Definition Statement.h:312
void set(unsigned index, BoostDecFloat16 value)
Convenience overload that binds a Boost 16-digit decimal floating-point value.
Definition Statement.h:1363
std::optional< TimeTz > getTimeTz(unsigned index)
Reads a time-of-day column with timezone.
Definition Statement.h:1865
void setTime(unsigned index, std::optional< Time > optValue)
Binds a time-of-day value without timezone or null.
Definition Statement.h:802
void set(unsigned index, std::string_view value)
Convenience overload that binds a textual value.
Definition Statement.h:1470
std::optional< BoostDecFloat34 > getBoostDecFloat34(unsigned index)
Reads a Boost-based 34-digit decimal floating-point column.
Definition Statement.h:1714
void setBlobId(unsigned index, std::optional< BlobId > optValue)
Binds a blob identifier to the specified parameter or null.
Definition Statement.h:1203
void setInt16(unsigned index, std::optional< std::int16_t > optValue)
Binds a 16-bit signed integer value or null.
Definition Statement.h:473
void setString(unsigned index, std::optional< std::string_view > optValue)
Binds a textual parameter or null, performing direct conversions where supported.
Definition Statement.h:1046
void set(unsigned index, std::int32_t value)
Convenience overload that binds a 32-bit signed integer.
Definition Statement.h:1280
std::optional< Time > getTime(unsigned index)
Reads a time-of-day column without timezone.
Definition Statement.h:1771
std::optional< Date > getDate(unsigned index)
Reads a date column.
Definition Statement.h:1724
FbRef< fb::IResultSet > getResultSetHandle() noexcept
Provides access to the underlying Firebird currently open result set handle, if any.
Definition Statement.h:288
bool isNull(unsigned index)
Reports whether the most recently fetched row has a null at the given column.
Definition Statement.h:1498
void set(unsigned index, double value)
Convenience overload that binds a double precision floating-point value.
Definition Statement.h:1346
void clearParameters()
Marks all bound parameters as null values.
Definition Statement.h:414
void set(unsigned index, ScaledInt64 value)
Convenience overload that binds a scaled 64-bit signed integer.
Definition Statement.h:1304
std::optional< OpaqueDate > getOpaqueDate(unsigned index)
Reads a raw date column in Firebird's representation.
Definition Statement.h:1748
T get()
Retrieves all output columns into a user-defined aggregate struct.
Definition Statement.h:2074
void setScaledInt32(unsigned index, std::optional< ScaledInt32 > optValue)
Binds a scaled 32-bit signed integer value or null.
Definition Statement.h:516
void set(unsigned index, std::int64_t value)
Convenience overload that binds a 64-bit signed integer.
Definition Statement.h:1296
void setTimestampTz(unsigned index, std::optional< TimestampTz > optValue)
Binds a timestamp value with timezone or null.
Definition Statement.h:985
std::optional< std::int64_t > getInt64(unsigned index)
Reads a 64-bit signed integer column.
Definition Statement.h:1572
FbRef< fb::IStatement > getStatementHandle() noexcept
Provides direct access to the underlying Firebird statement handle.
Definition Statement.h:279
std::optional< ScaledInt32 > getScaledInt32(unsigned index)
Reads a scaled 32-bit signed integer column.
Definition Statement.h:1562
void set(unsigned index, OpaqueTimestampTz value)
Convenience overload that binds a Firebird timestamp with timezone value.
Definition Statement.h:1462
void setOpaqueTime(unsigned index, std::optional< OpaqueTime > optValue)
Binds a raw time-of-day value in Firebird's representation or null.
Definition Statement.h:833
std::optional< float > getFloat(unsigned index)
Reads a single precision floating-point column.
Definition Statement.h:1638
std::optional< double > getDouble(unsigned index)
Reads a double precision floating-point column.
Definition Statement.h:1647
std::optional< OpaqueTimestampTz > getOpaqueTimestampTz(unsigned index)
Reads a raw timestamp-with-time-zone column in Firebird's representation.
Definition Statement.h:1936
std::optional< ScaledInt16 > getScaledInt16(unsigned index)
Reads a scaled 16-bit signed integer column.
Definition Statement.h:1543
std::optional< BlobId > getBlobId(unsigned index)
Reads a blob identifier column.
Definition Statement.h:1959
Statement(Statement &&o) noexcept
Transfers ownership of an existing prepared statement.
Definition Statement.h:220
void setInt64(unsigned index, std::optional< std::int64_t > optValue)
Binds a 64-bit signed integer value or null.
Definition Statement.h:531
std::optional< ScaledOpaqueInt128 > getScaledOpaqueInt128(unsigned index)
Reads a Firebird scaled 128-bit integer column.
Definition Statement.h:1591
FbRef< fb::IMessageMetadata > getOutputMetadata() noexcept
Returns the metadata describing columns produced by the statement.
Definition Statement.h:304
void set(unsigned index, TimeTz value)
Convenience overload that binds a Firebird time with timezone value.
Definition Statement.h:1438
void setOpaqueDecFloat16(unsigned index, std::optional< OpaqueDecFloat16 > optValue)
Binds a 16-digit decimal floating-point value in Firebird's representation or null.
Definition Statement.h:649
void setOpaqueInt128(unsigned index, std::optional< OpaqueInt128 > optValue)
Binds a raw 128-bit integer value in Firebird's representation or null.
Definition Statement.h:560
std::optional< OpaqueTimeTz > getOpaqueTimeTz(unsigned index)
Reads a raw time-of-day column with timezone in Firebird's representation.
Definition Statement.h:1889
void setBoostInt128(unsigned index, std::optional< BoostInt128 > optValue)
Binds a 128-bit integer value expressed with Boost.Multiprecision or null.
Definition Statement.h:591
void set(unsigned index, Date value)
Convenience overload that binds a Firebird date value.
Definition Statement.h:1390
void set(unsigned index, Timestamp value)
Convenience overload that binds a Firebird timestamp value.
Definition Statement.h:1422
void setOpaqueTimeTz(unsigned index, std::optional< OpaqueTimeTz > optValue)
Binds a raw time-of-day value with timezone in Firebird's representation or null.
Definition Statement.h:955
void setTimestamp(unsigned index, std::optional< Timestamp > optValue)
Binds a timestamp value without timezone or null.
Definition Statement.h:863
std::optional< std::string > getString(unsigned index)
Reads a textual column, applying number-to-string conversions when needed.
Definition Statement.h:1986
std::optional< std::int32_t > getInt32(unsigned index)
Reads a 32-bit signed integer column.
Definition Statement.h:1553
void set(unsigned index, OpaqueDecFloat16 value)
Convenience overload that binds a Firebird 16-digit decimal floating-point value.
Definition Statement.h:1354
void setOpaqueTimestampTz(unsigned index, std::optional< OpaqueTimestampTz > optValue)
Binds a raw timestamp value with timezone in Firebird's representation or null.
Definition Statement.h:1016
FbRef< fb::IMessageMetadata > getInputMetadata() noexcept
Returns the metadata describing prepared input parameters.
Definition Statement.h:296
~Statement() noexcept
Releases resources; ignores failures to keep destructor noexcept.
Definition Statement.h:245
void set(unsigned index, OpaqueTimeTz value)
Convenience overload that binds a Firebird time with timezone value.
Definition Statement.h:1446
void setBoostDecFloat16(unsigned index, std::optional< BoostDecFloat16 > optValue)
Binds a 16-digit decimal floating-point value using Boost.Multiprecision or null.
Definition Statement.h:680
std::optional< ScaledBoostInt128 > getScaledBoostInt128(unsigned index)
Reads a scaled Boost 128-bit integer column.
Definition Statement.h:1627
std::string getPlan()
Retrieves the structured textual plan if the server produced one.
void set(unsigned index, BlobId value)
Convenience overload that binds a blob identifier.
Definition Statement.h:1240
void set(unsigned index, BoostDecFloat34 value)
Convenience overload that binds a Boost 34-digit decimal floating-point value.
Definition Statement.h:1381
bool fetchNext()
Fetches the next row in the current result set.
void set(unsigned index, OpaqueDecFloat34 value)
Convenience overload that binds a Firebird 34-digit decimal floating-point value.
Definition Statement.h:1372
void set(unsigned index, Time value)
Convenience overload that binds a Firebird time value.
Definition Statement.h:1406
T get(unsigned index)
Retrieves a column using the most appropriate typed accessor specialization.
void set(unsigned index, ScaledInt16 value)
Convenience overload that binds a scaled 16-bit signed integer.
Definition Statement.h:1272
std::optional< BoostInt128 > getBoostInt128(unsigned index)
Reads a Boost 128-bit integer column.
Definition Statement.h:1617
std::optional< bool > getBool(unsigned index)
Reads a boolean column from the current row.
Definition Statement.h:1511
void setOpaqueDecFloat34(unsigned index, std::optional< OpaqueDecFloat34 > optValue)
Binds a 34-digit decimal floating-point value in Firebird's representation or null.
Definition Statement.h:695
void set(unsigned index, OpaqueTimestamp value)
Convenience overload that binds a Firebird timestamp value.
Definition Statement.h:1430
void setInt32(unsigned index, std::optional< std::int32_t > optValue)
Binds a 32-bit signed integer value or null.
Definition Statement.h:502
void set(unsigned index, std::optional< BlobId > value)
Convenience overload that binds an optional blob identifier.
Definition Statement.h:1248
void setBool(unsigned index, std::optional< bool > optValue)
Binds a boolean parameter value or null.
Definition Statement.h:443
void set(unsigned index, const V &value)
Sets a parameter from a variant value.
Definition Statement.h:2198
void set(unsigned index, TimestampTz value)
Convenience overload that binds a Firebird timestamp with timezone value.
Definition Statement.h:1454
void setDouble(unsigned index, std::optional< double > optValue)
Binds a double precision floating-point value or null.
Definition Statement.h:635
void set(unsigned index, OpaqueDate value)
Convenience overload that binds a Firebird date value.
Definition Statement.h:1398
void set(unsigned index, std::int16_t value)
Convenience overload that binds a 16-bit signed integer.
Definition Statement.h:1264
void set(unsigned index, std::optional< T > value)
Convenience template that forwards optional values to specialized overloads.
Definition Statement.h:1479
std::optional< OpaqueTimestamp > getOpaqueTimestamp(unsigned index)
Reads a raw timestamp column in Firebird's representation.
Definition Statement.h:1842
void setScaledInt64(unsigned index, std::optional< ScaledInt64 > optValue)
Binds a scaled 64-bit signed integer value or null.
Definition Statement.h:545
void set(unsigned index, std::nullopt_t)
Convenience overload that binds a null value.
Definition Statement.h:1232
void setFloat(unsigned index, std::optional< float > optValue)
Binds a single precision floating-point value or null.
Definition Statement.h:621
void setOpaqueDate(unsigned index, std::optional< OpaqueDate > optValue)
Binds a raw date value in Firebird's representation or null.
Definition Statement.h:772
bool fetchLast()
Positions the cursor on the last row.
bool execute(Transaction &transaction)
Executes a prepared statement using the supplied transaction.
void set(unsigned index, OpaqueTime value)
Convenience overload that binds a Firebird time value.
Definition Statement.h:1414
void set(unsigned index, ScaledInt32 value)
Convenience overload that binds a scaled 32-bit signed integer.
Definition Statement.h:1288
Statement(Attachment &attachment, Transaction &transaction, std::string_view sql, const StatementOptions &options={})
Prepares an SQL statement.
Definition Statement.cpp:34
bool fetchPrior()
Fetches the previous row in the current result set.
std::optional< std::int16_t > getInt16(unsigned index)
Reads a 16-bit signed integer column.
Definition Statement.h:1534
void set(unsigned index, ScaledBoostInt128 value)
Convenience overload that binds a scaled Boost-provided 128-bit integer.
Definition Statement.h:1329
bool fetchFirst()
Positions the cursor on the first row.
std::optional< TimestampTz > getTimestampTz(unsigned index)
Reads a timestamp-with-time-zone column.
Definition Statement.h:1912
void set(unsigned index, BoostInt128 value)
Convenience overload that binds a Boost-provided 128-bit integer.
Definition Statement.h:1321
std::optional< OpaqueDecFloat16 > getOpaqueDecFloat16(unsigned index)
Reads a Firebird 16-digit decimal floating-point column.
Definition Statement.h:1656
std::optional< Timestamp > getTimestamp(unsigned index)
Reads a timestamp column without timezone.
Definition Statement.h:1818
std::optional< BoostDecFloat16 > getBoostDecFloat16(unsigned index)
Reads a Boost-based 16-digit decimal floating-point column.
Definition Statement.h:1680
void setTimeTz(unsigned index, std::optional< TimeTz > optValue)
Binds a time-of-day value with timezone or null.
Definition Statement.h:924
void setBoostDecFloat34(unsigned index, std::optional< BoostDecFloat34 > optValue)
Binds a 34-digit decimal floating-point value using Boost.Multiprecision or null.
Definition Statement.h:726
std::optional< ScaledInt64 > getScaledInt64(unsigned index)
Reads a scaled 64-bit signed integer column.
Definition Statement.h:1581
void set(unsigned index, bool value)
Convenience overload that binds a boolean value.
Definition Statement.h:1256
void setOpaqueTimestamp(unsigned index, std::optional< OpaqueTimestamp > optValue)
Binds a raw timestamp value in Firebird's representation or null.
Definition Statement.h:894
void set(unsigned index, float value)
Convenience overload that binds a single precision floating-point value.
Definition Statement.h:1338
void set(const T &value)
Sets all input parameters from fields of a user-defined aggregate struct.
Definition Statement.h:2096
Represents a transaction in one or more Firebird databases.
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:146
@ 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.
StatementType
Distinguishes the semantic category of the prepared SQL statement.
Definition Statement.h:142
@ SAVEPOINT
Statement manages a savepoint.
@ PUT_SEGMENT
Statement writes a blob segment - legacy feature.
@ UPDATE
Server classified the statement as an UPDATE.
@ COMMIT
Statement commits a transaction.
@ ROLLBACK
Statement rolls back a transaction.
@ EXEC_PROCEDURE
Statement executes a stored procedure.
@ DELETE
Server classified the statement as a DELETE.
@ DDL
Statement performs data definition operations.
@ GET_SEGMENT
Statement reads a blob segment - legacy feature.
@ INSERT
Server classified the statement as an INSERT.
@ SELECT
Server classified the statement as a SELECT.
@ SET_GENERATOR
Statement sets a generator (sequence) value.
@ START_TRANSACTION
Statement starts a new transaction.
@ SELECT_FOR_UPDATE
Cursor-based SELECT that allows updates.
FB_I128 OpaqueInt128
Opaque 128-bit integer exposed by the Firebird API.
Definition types.h:200
std::chrono::year_month_day Date
Firebird SQL calendar date.
Definition types.h:108
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
std::chrono::hh_mm_ss< std::chrono::microseconds > Time
Firebird SQL time-of-day with microsecond resolution.
Definition types.h:113
Describes a parameter or column.
Definition Descriptor.h:247
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
Local time bound to a time zone.
Definition types.h:162
Timestamp bound to a time zone.
Definition types.h:183
Combined date and time with microsecond precision.
Definition types.h:119