fb-cpp 0.0.2
A modern C++ wrapper for the Firebird database API
Loading...
Searching...
No Matches
ParameterSetter.h
1/*
2 * MIT License
3 *
4 * Copyright (c) 2026 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_IMPL_PARAMETER_SETTER_H
26#define FBCPP_IMPL_PARAMETER_SETTER_H
27
28#include "../config.h"
29#include "../fb-api.h"
30#include "../Blob.h"
31#include "../Array.h"
32#include "../CalendarConverter.h"
33#include "../Client.h"
34#include "../Descriptor.h"
35#include "../Exception.h"
36#include "../NumericConverter.h"
37#include "../StructBinding.h"
38#include "../types.h"
39#include "../VariantTypeTraits.h"
40#include <algorithm>
41#include <cassert>
42#include <cerrno>
43#include <charconv>
44#include <cstddef>
45#include <cstdint>
46#include <cstdlib>
47#include <optional>
48#include <span>
49#include <string>
50#include <string_view>
51#include <stdexcept>
52#include <tuple>
53#include <type_traits>
54#include <utility>
55#include <variant>
56#include <vector>
57
58#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
59#include <boost/multiprecision/cpp_dec_float.hpp>
60#include <boost/multiprecision/cpp_int.hpp>
61#endif
62
63#if FB_CPP_USE_BOOST_DECIMAL != 0
64#include <boost/decimal.hpp>
65#endif
66
67
68namespace fbcpp::impl
69{
76 template <typename Derived, bool RequestMessageLayout>
77 class ParameterSetter
78 {
79 public:
83
87 void clearParameters()
88 {
89 validateSetter();
90
91 for (const auto& descriptor : descriptors())
92 {
93 if constexpr (RequestMessageLayout)
94 {
95 if (!descriptor.isNullable)
96 continue;
97 }
98
99 writeNullFlag(descriptor, FB_TRUE);
100 }
101 }
102
106 void setNull(unsigned index)
107 {
108 validateSetter();
109
110 const auto& descriptor = getDescriptor(index);
111
112 if constexpr (RequestMessageLayout)
113 {
114 if (!descriptor.isNullable)
115 throwNonNullable(index);
116 }
117
118 writeNullFlag(descriptor, FB_TRUE);
119 }
120
124 void setBool(unsigned index, std::optional<bool> optValue)
125 {
126 setExact(index, optValue, DescriptorAdjustedType::BOOLEAN, "bool",
127 [](std::byte* data, bool value) { *data = value ? std::byte{1} : std::byte{0}; });
128 }
129
133 void setInt16(unsigned index, std::optional<std::int16_t> optValue)
134 {
135 if (!optValue.has_value())
136 {
137 setNull(index);
138 return;
139 }
140
141 setNumber(index, DescriptorAdjustedType::INT16, optValue.value(), 0, "std::int16_t");
142 }
143
147 void setScaledInt16(unsigned index, std::optional<ScaledInt16> optValue)
148 {
149 if (!optValue.has_value())
150 {
151 setNull(index);
152 return;
153 }
154
155 const auto& value = optValue.value();
156 setNumber(index, DescriptorAdjustedType::INT16, value.value, value.scale, "ScaledInt16");
157 }
158
162 void setInt32(unsigned index, std::optional<std::int32_t> optValue)
163 {
164 if (!optValue.has_value())
165 {
166 setNull(index);
167 return;
168 }
169
170 setNumber(index, DescriptorAdjustedType::INT32, optValue.value(), 0, "std::int32_t");
171 }
172
176 void setScaledInt32(unsigned index, std::optional<ScaledInt32> optValue)
177 {
178 if (!optValue.has_value())
179 {
180 setNull(index);
181 return;
182 }
183
184 const auto& value = optValue.value();
185 setNumber(index, DescriptorAdjustedType::INT32, value.value, value.scale, "ScaledInt32");
186 }
187
191 void setInt64(unsigned index, std::optional<std::int64_t> optValue)
192 {
193 if (!optValue.has_value())
194 {
195 setNull(index);
196 return;
197 }
198
199 setNumber(index, DescriptorAdjustedType::INT64, optValue.value(), 0, "std::int64_t");
200 }
201
205 void setScaledInt64(unsigned index, std::optional<ScaledInt64> optValue)
206 {
207 if (!optValue.has_value())
208 {
209 setNull(index);
210 return;
211 }
212
213 const auto& value = optValue.value();
214 setNumber(index, DescriptorAdjustedType::INT64, value.value, value.scale, "ScaledInt64");
215 }
216
220 void setOpaqueInt128(unsigned index, std::optional<OpaqueInt128> optValue)
221 {
222 setExact(index, optValue, DescriptorAdjustedType::INT128, "OpaqueInt128",
223 [](std::byte* data, const OpaqueInt128& value) { *reinterpret_cast<OpaqueInt128*>(data) = value; });
224 }
225
229 void setScaledOpaqueInt128(unsigned index, std::optional<ScaledOpaqueInt128> optValue)
230 {
231 if (!optValue.has_value())
232 {
233 setNull(index);
234 return;
235 }
236
237 validateSetter();
238
239 const auto& value = optValue.value();
240 const auto& descriptor = getDescriptor(index);
241 auto* const data = message().data();
242
243 switch (descriptor.adjustedType)
244 {
246 if (value.scale == descriptor.scale)
247 *reinterpret_cast<OpaqueInt128*>(&data[descriptor.offset]) = value.value;
248 else
249 {
250 const auto valueString =
251 numericConverter().opaqueInt128ToString(&statusWrapper(), value.value, value.scale);
252 client()
253 .getInt128Util(&statusWrapper())
254 ->fromString(&statusWrapper(), descriptor.scale, valueString.c_str(),
255 reinterpret_cast<OpaqueInt128*>(&data[descriptor.offset]));
256 }
257 break;
258
259 default:
260 throwInvalidType("ScaledOpaqueInt128", descriptor.adjustedType);
261 }
262
263 writeNotNullFlag(descriptor);
264 }
265
266#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
270 void setBoostInt128(unsigned index, std::optional<BoostInt128> optValue)
271 {
272 if (!optValue.has_value())
273 {
274 setNull(index);
275 return;
276 }
277
278 setNumber(index, DescriptorAdjustedType::INT128, optValue.value(), 0, "BoostInt128");
279 }
280
284 void setScaledBoostInt128(unsigned index, std::optional<ScaledBoostInt128> optValue)
285 {
286 if (!optValue.has_value())
287 {
288 setNull(index);
289 return;
290 }
291
292 const auto& value = optValue.value();
293 setNumber(index, DescriptorAdjustedType::INT128, value.value, value.scale, "ScaledBoostInt128");
294 }
295#endif
296
300 void setFloat(unsigned index, std::optional<float> optValue)
301 {
302 if (!optValue.has_value())
303 {
304 setNull(index);
305 return;
306 }
307
308 setNumber(index, DescriptorAdjustedType::FLOAT, optValue.value(), 0, "float");
309 }
310
314 void setDouble(unsigned index, std::optional<double> optValue)
315 {
316 if (!optValue.has_value())
317 {
318 setNull(index);
319 return;
320 }
321
322 setNumber(index, DescriptorAdjustedType::DOUBLE, optValue.value(), 0, "double");
323 }
324
328 void setOpaqueDecFloat16(unsigned index, std::optional<OpaqueDecFloat16> optValue)
329 {
330 setExact(index, optValue, DescriptorAdjustedType::DECFLOAT16, "OpaqueDecFloat16",
331 [](std::byte* data, const OpaqueDecFloat16& value)
332 { *reinterpret_cast<OpaqueDecFloat16*>(data) = value; });
333 }
334
335#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
339 void setBoostDecFloat16(unsigned index, std::optional<BoostDecFloat16> optValue)
340 {
341 if (!optValue.has_value())
342 {
343 setNull(index);
344 return;
345 }
346
347 setNumber(index, DescriptorAdjustedType::DECFLOAT16, optValue.value(), 0, "BoostDecFloat16");
348 }
349#endif
350
351#if FB_CPP_USE_BOOST_DECIMAL != 0
355 void setBoostDecimal32(unsigned index, std::optional<BoostDecimal32> optValue)
356 {
357 if (!optValue.has_value())
358 {
359 setNull(index);
360 return;
361 }
362
363 setNumber(index, DescriptorAdjustedType::DECFLOAT16, optValue.value(), 0, "BoostDecimal32");
364 }
365
369 void setBoostDecimal64(unsigned index, std::optional<BoostDecimal64> optValue)
370 {
371 if (!optValue.has_value())
372 {
373 setNull(index);
374 return;
375 }
376
377 setNumber(index, DescriptorAdjustedType::DECFLOAT16, optValue.value(), 0, "BoostDecimal64");
378 }
379#endif
380
384 void setOpaqueDecFloat34(unsigned index, std::optional<OpaqueDecFloat34> optValue)
385 {
386 setExact(index, optValue, DescriptorAdjustedType::DECFLOAT34, "OpaqueDecFloat34",
387 [](std::byte* data, const OpaqueDecFloat34& value)
388 { *reinterpret_cast<OpaqueDecFloat34*>(data) = value; });
389 }
390
391#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
395 void setBoostDecFloat34(unsigned index, std::optional<BoostDecFloat34> optValue)
396 {
397 if (!optValue.has_value())
398 {
399 setNull(index);
400 return;
401 }
402
403 setNumber(index, DescriptorAdjustedType::DECFLOAT34, optValue.value(), 0, "BoostDecFloat34");
404 }
405#endif
406
407#if FB_CPP_USE_BOOST_DECIMAL != 0
411 void setBoostDecimal128(unsigned index, std::optional<BoostDecimal128> optValue)
412 {
413 if (!optValue.has_value())
414 {
415 setNull(index);
416 return;
417 }
418
419 setNumber(index, DescriptorAdjustedType::DECFLOAT34, optValue.value(), 0, "BoostDecimal128");
420 }
421#endif
422
426 void setDate(unsigned index, std::optional<Date> optValue)
427 {
428 setExact(index, optValue, DescriptorAdjustedType::DATE, "Date", [this](std::byte* data, const Date& value)
429 { *reinterpret_cast<OpaqueDate*>(data) = calendarConverter().dateToOpaqueDate(value); });
430 }
431
435 void setOpaqueDate(unsigned index, std::optional<OpaqueDate> optValue)
436 {
437 setExact(index, optValue, DescriptorAdjustedType::DATE, "OpaqueDate",
438 [](std::byte* data, const OpaqueDate& value) { *reinterpret_cast<OpaqueDate*>(data) = value; });
439 }
440
444 void setTime(unsigned index, std::optional<Time> optValue)
445 {
446 setExact(index, optValue, DescriptorAdjustedType::TIME, "Time", [this](std::byte* data, const Time& value)
447 { *reinterpret_cast<OpaqueTime*>(data) = calendarConverter().timeToOpaqueTime(value); });
448 }
449
453 void setOpaqueTime(unsigned index, std::optional<OpaqueTime> optValue)
454 {
455 setExact(index, optValue, DescriptorAdjustedType::TIME, "OpaqueTime",
456 [](std::byte* data, const OpaqueTime& value) { *reinterpret_cast<OpaqueTime*>(data) = value; });
457 }
458
462 void setTimestamp(unsigned index, std::optional<Timestamp> optValue)
463 {
464 setExact(index, optValue, DescriptorAdjustedType::TIMESTAMP, "Timestamp",
465 [this](std::byte* data, const Timestamp& value)
466 { *reinterpret_cast<OpaqueTimestamp*>(data) = calendarConverter().timestampToOpaqueTimestamp(value); });
467 }
468
472 void setOpaqueTimestamp(unsigned index, std::optional<OpaqueTimestamp> optValue)
473 {
474 setExact(index, optValue, DescriptorAdjustedType::TIMESTAMP, "OpaqueTimestamp",
475 [](std::byte* data, const OpaqueTimestamp& value)
476 { *reinterpret_cast<OpaqueTimestamp*>(data) = value; });
477 }
478
482 void setTimeTz(unsigned index, std::optional<TimeTz> optValue)
483 {
484 setExact(index, optValue, DescriptorAdjustedType::TIME_TZ, "TimeTz",
485 [this](std::byte* data, const TimeTz& value)
486 {
487 *reinterpret_cast<OpaqueTimeTz*>(data) =
488 calendarConverter().timeTzToOpaqueTimeTz(&statusWrapper(), value);
489 });
490 }
491
495 void setOpaqueTimeTz(unsigned index, std::optional<OpaqueTimeTz> optValue)
496 {
497 setExact(index, optValue, DescriptorAdjustedType::TIME_TZ, "OpaqueTimeTz",
498 [](std::byte* data, const OpaqueTimeTz& value) { *reinterpret_cast<OpaqueTimeTz*>(data) = value; });
499 }
500
504 void setTimestampTz(unsigned index, std::optional<TimestampTz> optValue)
505 {
506 setExact(index, optValue, DescriptorAdjustedType::TIMESTAMP_TZ, "TimestampTz",
507 [this](std::byte* data, const TimestampTz& value)
508 {
509 *reinterpret_cast<OpaqueTimestampTz*>(data) =
510 calendarConverter().timestampTzToOpaqueTimestampTz(&statusWrapper(), value);
511 });
512 }
513
517 void setOpaqueTimestampTz(unsigned index, std::optional<OpaqueTimestampTz> optValue)
518 {
519 setExact(index, optValue, DescriptorAdjustedType::TIMESTAMP_TZ, "OpaqueTimestampTz",
520 [](std::byte* data, const OpaqueTimestampTz& value)
521 { *reinterpret_cast<OpaqueTimestampTz*>(data) = value; });
522 }
523
527 void setString(unsigned index, std::optional<std::string_view> optValue)
528 {
529 if (!optValue.has_value())
530 {
531 setNull(index);
532 return;
533 }
534
535 validateSetter();
536
537 auto& clientValue = client();
538 const auto value = optValue.value();
539 const auto& descriptor = getDescriptor(index);
540 auto* const data = message().data();
541 const auto descriptorData = &data[descriptor.offset];
542
543 switch (descriptor.adjustedType)
544 {
546 data[descriptor.offset] = numericConverter().stringToBoolean(value);
547 break;
548
552 {
553 std::string strValue(value);
554 int scale = 0;
555
556 if (const auto dotPos = strValue.find_last_of('.'); dotPos != std::string_view::npos)
557 {
558 for (auto pos = dotPos + 1; pos < strValue.size(); ++pos)
559 {
560 const char c = value[pos];
561
562 if (c < '0' || c > '9')
563 break;
564
565 --scale;
566 }
567
568 strValue.erase(dotPos, 1);
569 }
570
571 static_assert(sizeof(long long) == sizeof(std::int64_t));
572 std::int64_t intValue;
573 const auto convResult =
574 std::from_chars(strValue.data(), strValue.data() + strValue.size(), intValue);
575 if (convResult.ec != std::errc{} || convResult.ptr != strValue.data() + strValue.size())
576 numericConverter().throwConversionErrorFromString(strValue);
577 auto scaledValue = ScaledInt64{intValue, scale};
578
579 if (scale != descriptor.scale)
580 {
581 scaledValue.value =
582 numericConverter().template numberToNumber<std::int64_t>(scaledValue, descriptor.scale);
583 scaledValue.scale = descriptor.scale;
584 }
585
586 setScaledInt64(index, scaledValue);
587 return;
588 }
589
591 {
592 std::string strValue(value);
593 clientValue.getInt128Util(&statusWrapper())
594 ->fromString(&statusWrapper(), descriptor.scale, strValue.c_str(),
595 reinterpret_cast<OpaqueInt128*>(descriptorData));
596 break;
597 }
598
601 {
602 double doubleValue;
603#if defined(__APPLE__)
604 errno = 0;
605 std::string valueString{value};
606 char* parseEnd = nullptr;
607 doubleValue = std::strtod(valueString.c_str(), &parseEnd);
608 if (parseEnd != valueString.c_str() + valueString.size() || errno == ERANGE)
609 numericConverter().throwConversionErrorFromString(std::move(valueString));
610#else
611 const auto convResult = std::from_chars(value.data(), value.data() + value.size(), doubleValue);
612 if (convResult.ec != std::errc{} || convResult.ptr != value.data() + value.size())
613 numericConverter().throwConversionErrorFromString(std::string{value});
614#endif
615 setDouble(index, doubleValue);
616 return;
617 }
618
620 *reinterpret_cast<OpaqueDate*>(descriptorData) = calendarConverter().stringToOpaqueDate(value);
621 break;
622
624 *reinterpret_cast<OpaqueTime*>(descriptorData) = calendarConverter().stringToOpaqueTime(value);
625 break;
626
628 *reinterpret_cast<OpaqueTimestamp*>(descriptorData) =
629 calendarConverter().stringToOpaqueTimestamp(value);
630 break;
631
633 *reinterpret_cast<OpaqueTimeTz*>(descriptorData) =
634 calendarConverter().stringToOpaqueTimeTz(&statusWrapper(), value);
635 break;
636
638 *reinterpret_cast<OpaqueTimestampTz*>(descriptorData) =
639 calendarConverter().stringToOpaqueTimestampTz(&statusWrapper(), value);
640 break;
641
643 {
644 std::string strValue{value};
645 clientValue.getDecFloat16Util(&statusWrapper())
646 ->fromString(
647 &statusWrapper(), strValue.c_str(), reinterpret_cast<OpaqueDecFloat16*>(descriptorData));
648 break;
649 }
650
652 {
653 std::string strValue{value};
654 clientValue.getDecFloat34Util(&statusWrapper())
655 ->fromString(
656 &statusWrapper(), strValue.c_str(), reinterpret_cast<OpaqueDecFloat34*>(descriptorData));
657 break;
658 }
659
661 if (value.length() > stringCapacity(descriptor))
662 {
663 static constexpr std::intptr_t STATUS_STRING_TRUNCATION[] = {
664 isc_arith_except,
665 isc_string_truncation,
666 isc_arg_end,
667 };
668
669 throw DatabaseException(clientValue, STATUS_STRING_TRUNCATION);
670 }
671
672 *reinterpret_cast<std::uint16_t*>(descriptorData) = static_cast<std::uint16_t>(value.length());
673 std::copy(value.begin(), value.end(),
674 reinterpret_cast<char*>(&data[descriptor.offset + sizeof(std::uint16_t)]));
675 break;
676
677 default:
678 throwInvalidType("std::string_view", descriptor.adjustedType);
679 }
680
681 writeNotNullFlag(descriptor);
682 }
683
687 void setBytes(unsigned index, std::span<const std::byte> value)
688 {
689 validateSetter();
690
691 const auto& descriptor = getDescriptor(index);
692 auto* const data = message().data();
693
694 switch (descriptor.adjustedType)
695 {
697 if (value.size() > stringCapacity(descriptor))
698 {
699 static constexpr std::intptr_t STATUS_STRING_TRUNCATION[] = {
700 isc_arith_except,
701 isc_string_truncation,
702 isc_arg_end,
703 };
704
705 throw DatabaseException(client(), STATUS_STRING_TRUNCATION);
706 }
707
708 *reinterpret_cast<std::uint16_t*>(&data[descriptor.offset]) =
709 static_cast<std::uint16_t>(value.size());
710 std::copy(value.begin(), value.end(), &data[descriptor.offset + sizeof(std::uint16_t)]);
711 break;
712
713 default:
714 throwInvalidType("std::span<const std::byte>", descriptor.adjustedType);
715 }
716
717 writeNotNullFlag(descriptor);
718 }
719
723 void setBytes(unsigned index, const std::vector<std::byte>& value)
724 {
725 setBytes(index, std::span<const std::byte>{value});
726 }
727
731 void setBytes(unsigned index, std::optional<std::vector<std::byte>> optValue)
732 {
733 if (!optValue.has_value())
734 {
735 setNull(index);
736 return;
737 }
738
739 setBytes(index, std::span<const std::byte>{optValue.value()});
740 }
741
745 void setBytes(unsigned index, std::nullopt_t)
746 {
747 setNull(index);
748 }
749
753 void setBlobId(unsigned index, std::optional<BlobId> optValue)
754 {
755 setExact(index, optValue, DescriptorAdjustedType::BLOB, "BlobId",
756 [](std::byte* data, const BlobId& value) { *reinterpret_cast<ISC_QUAD*>(data) = value.id; });
757 }
758
762 void setArrayId(unsigned index, std::optional<ArrayId> optValue)
763 {
764 setExact(index, optValue, DescriptorAdjustedType::ARRAY, "ArrayId",
765 [](std::byte* data, const ArrayId& value) { *reinterpret_cast<ISC_QUAD*>(data) = value.id; });
766 }
767
771
775 void set(unsigned index, std::nullopt_t)
776 {
777 setNull(index);
778 }
779
783 void set(unsigned index, BlobId value)
784 {
785 setBlobId(index, value);
786 }
787
791 void set(unsigned index, std::optional<BlobId> value)
792 {
793 setBlobId(index, value);
794 }
795
799 void set(unsigned index, ArrayId value)
800 {
801 setArrayId(index, value);
802 }
803
807 void set(unsigned index, std::optional<ArrayId> value)
808 {
809 setArrayId(index, value);
810 }
811
815 void set(unsigned index, bool value)
816 {
817 setBool(index, value);
818 }
819
823 void set(unsigned index, std::int16_t value)
824 {
825 setInt16(index, value);
826 }
827
831 void set(unsigned index, ScaledInt16 value)
832 {
833 setScaledInt16(index, value);
834 }
835
839 void set(unsigned index, std::int32_t value)
840 {
841 setInt32(index, value);
842 }
843
847 void set(unsigned index, ScaledInt32 value)
848 {
849 setScaledInt32(index, value);
850 }
851
855 void set(unsigned index, std::int64_t value)
856 {
857 setInt64(index, value);
858 }
859
863 void set(unsigned index, ScaledInt64 value)
864 {
865 setScaledInt64(index, value);
866 }
867
871 void set(unsigned index, OpaqueInt128 value)
872 {
873 setOpaqueInt128(index, value);
874 }
875
879 void set(unsigned index, ScaledOpaqueInt128 value)
880 {
881 setScaledOpaqueInt128(index, value);
882 }
883
884#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
888 void set(unsigned index, BoostInt128 value)
889 {
890 setBoostInt128(index, value);
891 }
892
896 void set(unsigned index, ScaledBoostInt128 value)
897 {
898 setScaledBoostInt128(index, value);
899 }
900#endif
901
905 void set(unsigned index, float value)
906 {
907 setFloat(index, value);
908 }
909
913 void set(unsigned index, double value)
914 {
915 setDouble(index, value);
916 }
917
921 void set(unsigned index, OpaqueDecFloat16 value)
922 {
923 setOpaqueDecFloat16(index, value);
924 }
925
926#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
930 void set(unsigned index, BoostDecFloat16 value)
931 {
932 setBoostDecFloat16(index, value);
933 }
934#endif
935
936#if FB_CPP_USE_BOOST_DECIMAL != 0
940 void set(unsigned index, BoostDecimal32 value)
941 {
942 setBoostDecimal32(index, value);
943 }
944
948 void set(unsigned index, BoostDecimal64 value)
949 {
950 setBoostDecimal64(index, value);
951 }
952#endif
953
957 void set(unsigned index, OpaqueDecFloat34 value)
958 {
959 setOpaqueDecFloat34(index, value);
960 }
961
962#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
966 void set(unsigned index, BoostDecFloat34 value)
967 {
968 setBoostDecFloat34(index, value);
969 }
970#endif
971
972#if FB_CPP_USE_BOOST_DECIMAL != 0
976 void set(unsigned index, BoostDecimal128 value)
977 {
978 setBoostDecimal128(index, value);
979 }
980#endif
981
985 void set(unsigned index, Date value)
986 {
987 setDate(index, value);
988 }
989
993 void set(unsigned index, OpaqueDate value)
994 {
995 setOpaqueDate(index, value);
996 }
997
1001 void set(unsigned index, Time value)
1002 {
1003 setTime(index, value);
1004 }
1005
1009 void set(unsigned index, OpaqueTime value)
1010 {
1011 setOpaqueTime(index, value);
1012 }
1013
1017 void set(unsigned index, Timestamp value)
1018 {
1019 setTimestamp(index, value);
1020 }
1021
1025 void set(unsigned index, OpaqueTimestamp value)
1026 {
1027 setOpaqueTimestamp(index, value);
1028 }
1029
1033 void set(unsigned index, TimeTz value)
1034 {
1035 setTimeTz(index, value);
1036 }
1037
1041 void set(unsigned index, OpaqueTimeTz value)
1042 {
1043 setOpaqueTimeTz(index, value);
1044 }
1045
1049 void set(unsigned index, TimestampTz value)
1050 {
1051 setTimestampTz(index, value);
1052 }
1053
1057 void set(unsigned index, OpaqueTimestampTz value)
1058 {
1059 setOpaqueTimestampTz(index, value);
1060 }
1061
1065 void set(unsigned index, std::string_view value)
1066 {
1067 setString(index, value);
1068 }
1069
1073 void set(unsigned index, const std::vector<std::byte>& value)
1074 {
1075 setBytes(index, value);
1076 }
1077
1081 void set(unsigned index, std::span<const std::byte> value)
1082 {
1083 setBytes(index, value);
1084 }
1085
1089 template <typename T>
1090 void set(unsigned index, std::optional<T> value)
1091 {
1092 if (value.has_value())
1093 set(index, value.value());
1094 else
1095 setNull(index);
1096 }
1097
1101
1105 template <Aggregate T>
1106 void set(const T& value)
1107 {
1108 using namespace reflection;
1109
1110 constexpr std::size_t N = fieldCountV<T>;
1111
1112 if (N != descriptors().size())
1113 {
1114 throw FbCppException("Struct field count (" + std::to_string(N) +
1115 ") does not match input parameter count (" + std::to_string(descriptors().size()) + ")");
1116 }
1117
1118 setStruct(value, std::make_index_sequence<N>{});
1119 }
1120
1124 template <TupleLike T>
1125 void set(const T& value)
1126 {
1127 constexpr std::size_t N = std::tuple_size_v<T>;
1128
1129 if (N != descriptors().size())
1130 {
1131 throw FbCppException("Tuple element count (" + std::to_string(N) +
1132 ") does not match input parameter count (" + std::to_string(descriptors().size()) + ")");
1133 }
1134
1135 setTuple(value, std::make_index_sequence<N>{});
1136 }
1137
1141 template <VariantLike V>
1142 void set(unsigned index, const V& value)
1143 {
1144 using namespace reflection;
1145
1146 static_assert(variantAlternativesSupportedV<V>,
1147 "Variant contains unsupported types. All variant alternatives must be types supported by fb-cpp "
1148 "(e.g., std::int32_t, std::string, Date, ScaledOpaqueInt128, etc.). Check VariantTypeTraits.h for "
1149 "the complete list of supported types.");
1150
1151 std::visit(
1152 [this, index](const auto& v)
1153 {
1154 using T = std::decay_t<decltype(v)>;
1155
1156 if constexpr (std::is_same_v<T, std::monostate>)
1157 setNull(index);
1158 else
1159 set(index, v);
1160 },
1161 value);
1162 }
1163
1167
1168 private:
1169 Derived& owner() noexcept
1170 {
1171 return static_cast<Derived&>(*this);
1172 }
1173
1174 const Derived& owner() const noexcept
1175 {
1176 return static_cast<const Derived&>(*this);
1177 }
1178
1179 const std::vector<Descriptor>& descriptors() const noexcept
1180 {
1181 return owner().getSetterDescriptors();
1182 }
1183
1184 std::vector<std::byte>& message() noexcept
1185 {
1186 return owner().getSetterMessage();
1187 }
1188
1189 Client& client() noexcept
1190 {
1191 return owner().getSetterClient();
1192 }
1193
1194 impl::StatusWrapper& statusWrapper() noexcept
1195 {
1196 return owner().statusWrapper;
1197 }
1198
1199 impl::NumericConverter& numericConverter() noexcept
1200 {
1201 return owner().numericConverter;
1202 }
1203
1204 impl::CalendarConverter& calendarConverter() noexcept
1205 {
1206 return owner().calendarConverter;
1207 }
1208
1209 void validateSetter() const noexcept
1210 {
1211 owner().validateSetter();
1212 }
1213
1214 const Descriptor& getDescriptor(unsigned index) const
1215 {
1216 if (index >= descriptors().size())
1217 throw std::out_of_range("index out of range");
1218
1219 return descriptors()[index];
1220 }
1221
1222 template <typename T, typename Writer>
1223 void setExact(unsigned index, std::optional<T> optValue, DescriptorAdjustedType expectedType,
1224 const char* typeName, Writer writer)
1225 {
1226 if (!optValue.has_value())
1227 {
1228 setNull(index);
1229 return;
1230 }
1231
1232 validateSetter();
1233
1234 const auto& descriptor = getDescriptor(index);
1235
1236 if (descriptor.adjustedType != expectedType)
1237 throwInvalidType(typeName, descriptor.adjustedType);
1238
1239 writer(&message()[descriptor.offset], optValue.value());
1240 writeNotNullFlag(descriptor);
1241 }
1242
1243#if FB_CPP_USE_BOOST_DECIMAL != 0
1244 template <typename T>
1245 void setDecimalNumber(unsigned index, T value, std::optional<int>& descriptorScale, const char* typeName)
1246 {
1247 const auto& descriptor = getDescriptor(index);
1248 auto* const data = message().data();
1249 const auto descriptorData = &data[descriptor.offset];
1250
1251 switch (descriptor.adjustedType)
1252 {
1254 *reinterpret_cast<std::int16_t*>(descriptorData) =
1255 numericConverter().template numberToNumber<std::int16_t>(value, descriptorScale.value());
1256 break;
1257
1259 *reinterpret_cast<std::int32_t*>(descriptorData) =
1260 numericConverter().template numberToNumber<std::int32_t>(value, descriptorScale.value());
1261 break;
1262
1264 *reinterpret_cast<std::int64_t*>(descriptorData) =
1265 numericConverter().template numberToNumber<std::int64_t>(value, descriptorScale.value());
1266 break;
1267
1268#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1270 {
1271 const auto int128Value =
1272 numericConverter().template numberToNumber<BoostInt128>(value, descriptorScale.value());
1273 *reinterpret_cast<OpaqueInt128*>(descriptorData) =
1274 numericConverter().boostInt128ToOpaqueInt128(&statusWrapper(), int128Value);
1275 break;
1276 }
1277#endif
1278
1280 *reinterpret_cast<float*>(descriptorData) =
1281 numericConverter().template numberToNumber<float>(value);
1282 break;
1283
1285 *reinterpret_cast<double*>(descriptorData) =
1286 numericConverter().template numberToNumber<double>(value);
1287 break;
1288
1290 {
1291 const auto decimalValue = numericConverter().template numberToNumber<BoostDecimal64>(value);
1292 *reinterpret_cast<OpaqueDecFloat16*>(descriptorData) =
1293 numericConverter().boostDecimal64ToOpaqueDecFloat16(&statusWrapper(), decimalValue);
1294 break;
1295 }
1296
1298 {
1299 const auto decimalValue = numericConverter().template numberToNumber<BoostDecimal128>(value);
1300 *reinterpret_cast<OpaqueDecFloat34*>(descriptorData) =
1301 numericConverter().boostDecimal128ToOpaqueDecFloat34(&statusWrapper(), decimalValue);
1302 break;
1303 }
1304
1305 default:
1306 throwInvalidType(typeName, descriptor.adjustedType);
1307 }
1308 }
1309#endif
1310
1311 template <typename T>
1312 void setNumber(unsigned index, DescriptorAdjustedType valueType, T value, int scale, const char* typeName)
1313 {
1314 validateSetter();
1315
1316 const auto& descriptor = getDescriptor(index);
1317 auto* const data = message().data();
1318 const auto descriptorData = &data[descriptor.offset];
1319 std::optional<int> descriptorScale{descriptor.scale};
1320
1321#if FB_CPP_USE_BOOST_DECIMAL != 0
1322 if constexpr (std::is_same_v<T, BoostDecimal32> || std::is_same_v<T, BoostDecimal64> ||
1323 std::is_same_v<T, BoostDecimal128>)
1324 {
1325 setDecimalNumber(index, value, descriptorScale, typeName);
1326 writeNotNullFlag(descriptor);
1327 return;
1328 }
1329#endif
1330
1331 Descriptor valueDescriptor;
1332 valueDescriptor.adjustedType = valueType;
1333 valueDescriptor.scale = scale;
1334
1335 const auto valueAddress = reinterpret_cast<const std::byte*>(&value);
1336
1337 switch (descriptor.adjustedType)
1338 {
1340 *reinterpret_cast<std::int16_t*>(descriptorData) =
1341 convertNumber<std::int16_t>(valueDescriptor, valueAddress, descriptorScale, "std::int16_t");
1342 break;
1343
1345 *reinterpret_cast<std::int32_t*>(descriptorData) =
1346 convertNumber<std::int32_t>(valueDescriptor, valueAddress, descriptorScale, "std::int32_t");
1347 break;
1348
1350 *reinterpret_cast<std::int64_t*>(descriptorData) =
1351 convertNumber<std::int64_t>(valueDescriptor, valueAddress, descriptorScale, "std::int64_t");
1352 break;
1353
1354#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1356 {
1357 const auto boostInt128 =
1358 convertNumber<BoostInt128>(valueDescriptor, valueAddress, descriptorScale, "BoostInt128");
1359 *reinterpret_cast<OpaqueInt128*>(descriptorData) =
1360 numericConverter().boostInt128ToOpaqueInt128(&statusWrapper(), boostInt128);
1361 break;
1362 }
1363#endif
1364
1366 *reinterpret_cast<float*>(descriptorData) =
1367 convertNumber<float>(valueDescriptor, valueAddress, descriptorScale, "float");
1368 break;
1369
1371 *reinterpret_cast<double*>(descriptorData) =
1372 convertNumber<double>(valueDescriptor, valueAddress, descriptorScale, "double");
1373 break;
1374
1375#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1377 {
1378 const auto boostDecFloat16 = convertNumber<BoostDecFloat16>(
1379 valueDescriptor, valueAddress, descriptorScale, "BoostDecFloat16");
1380 *reinterpret_cast<OpaqueDecFloat16*>(descriptorData) =
1381 numericConverter().boostDecFloat16ToOpaqueDecFloat16(&statusWrapper(), boostDecFloat16);
1382 break;
1383 }
1384
1386 {
1387 const auto boostDecFloat34 = convertNumber<BoostDecFloat34>(
1388 valueDescriptor, valueAddress, descriptorScale, "BoostDecFloat34");
1389 *reinterpret_cast<OpaqueDecFloat34*>(descriptorData) =
1390 numericConverter().boostDecFloat34ToOpaqueDecFloat34(&statusWrapper(), boostDecFloat34);
1391 break;
1392 }
1393#endif
1394
1395 default:
1396 throwInvalidType(typeName, descriptor.adjustedType);
1397 }
1398
1399 writeNotNullFlag(descriptor);
1400 }
1401
1402 [[noreturn]] static void throwInvalidType(const char* actualType, DescriptorAdjustedType descriptorType)
1403 {
1404 throw FbCppException("Invalid type: actual type " + std::string(actualType) + ", descriptor type " +
1405 std::to_string(static_cast<unsigned>(descriptorType)));
1406 }
1407
1408 [[noreturn]] static void throwNonNullable(unsigned index)
1409 {
1410 throw FbCppException("Field " + std::to_string(index) + " is not nullable");
1411 }
1412
1413 void writeNullFlag(const Descriptor& descriptor, std::int16_t value)
1414 {
1415 if constexpr (!RequestMessageLayout)
1416 {
1417 *reinterpret_cast<std::int16_t*>(&message()[descriptor.nullOffset]) = value;
1418 }
1419 else if (descriptor.isNullable)
1420 *reinterpret_cast<std::int16_t*>(&message()[descriptor.nullOffset]) = value;
1421 }
1422
1423 void writeNotNullFlag(const Descriptor& descriptor)
1424 {
1425 writeNullFlag(descriptor, FB_FALSE);
1426 }
1427
1428 unsigned stringCapacity(const Descriptor& descriptor) const noexcept
1429 {
1430 if constexpr (RequestMessageLayout)
1431 return descriptor.length - sizeof(std::uint16_t);
1432 else
1433 return descriptor.length;
1434 }
1435
1436 template <typename T, std::size_t... Is>
1437 void setStruct(const T& value, std::index_sequence<Is...>)
1438 {
1439 using namespace reflection;
1440
1441 const auto tuple = toTupleRef(value);
1442 (set(static_cast<unsigned>(Is), std::get<Is>(tuple)), ...);
1443 }
1444
1445 template <typename T, std::size_t... Is>
1446 void setTuple(const T& value, std::index_sequence<Is...>)
1447 {
1448 (set(static_cast<unsigned>(Is), std::get<Is>(value)), ...);
1449 }
1450
1451 template <typename T>
1452 T convertNumber(
1453 const Descriptor& descriptor, const std::byte* data, std::optional<int>& toScale, const char* toTypeName)
1454 {
1455 if (!toScale.has_value())
1456 {
1457 switch (descriptor.adjustedType)
1458 {
1463 throwInvalidType(toTypeName, descriptor.adjustedType);
1464
1465 default:
1466 break;
1467 }
1468
1469 toScale = descriptor.scale;
1470 }
1471
1472 switch (descriptor.adjustedType)
1473 {
1475 return numericConverter().template numberToNumber<T>(
1476 ScaledInt16{*reinterpret_cast<const std::int16_t*>(data), descriptor.scale}, toScale.value());
1477
1479 return numericConverter().template numberToNumber<T>(
1480 ScaledInt32{*reinterpret_cast<const std::int32_t*>(data), descriptor.scale}, toScale.value());
1481
1483 return numericConverter().template numberToNumber<T>(
1484 ScaledInt64{*reinterpret_cast<const std::int64_t*>(data), descriptor.scale}, toScale.value());
1485
1486#if FB_CPP_USE_BOOST_MULTIPRECISION != 0
1488 return numericConverter().template numberToNumber<T>(
1489 ScaledBoostInt128{*reinterpret_cast<const BoostInt128*>(data), descriptor.scale},
1490 toScale.value());
1491
1493 return numericConverter().template numberToNumber<T>(
1494 *reinterpret_cast<const BoostDecFloat16*>(data), toScale.value());
1495
1497 return numericConverter().template numberToNumber<T>(
1498 *reinterpret_cast<const BoostDecFloat34*>(data), toScale.value());
1499#endif
1500
1502 return numericConverter().template numberToNumber<T>(
1503 *reinterpret_cast<const float*>(data), toScale.value());
1504
1506 return numericConverter().template numberToNumber<T>(
1507 *reinterpret_cast<const double*>(data), toScale.value());
1508
1509 default:
1510 throwInvalidType(toTypeName, descriptor.adjustedType);
1511 }
1512 }
1513 };
1514} // namespace fbcpp::impl
1515
1516
1517#endif // FBCPP_IMPL_PARAMETER_SETTER_H
ScaledNumber< OpaqueInt128 > ScaledOpaqueInt128
Scaled Firebird opaque 128-bit integer.
Definition types.h:246
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
boost::decimal::decimal32_t BoostDecimal32
7-digit decimal floating point using Boost.Decimal.
Definition types.h:123
DescriptorAdjustedType
Descriptor adjusted type.
Definition Descriptor.h:161
@ 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.
@ ARRAY
Array value represented by an array identifier.
@ 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
std::chrono::year_month_day Date
Firebird SQL calendar date.
Definition types.h:139
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
std::chrono::hh_mm_ss< std::chrono::microseconds > Time
Firebird SQL time-of-day with microsecond resolution.
Definition types.h:144