fb-cpp 0.0.2
A modern C++ wrapper for the Firebird database API
Loading...
Searching...
No Matches
Exception.cpp
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#include "Exception.h"
26#include "Client.h"
27#include <string>
28#include <vector>
29#include <cassert>
30
31using namespace fbcpp;
32using namespace fbcpp::impl;
33
34
35fb::IStatus* StatusWrapper::getStatus() const
36{
37 if (!status)
38 {
39 status = client->newStatus().release();
40 statusOwner = true;
41 }
42
43 return status;
44}
45
46void StatusWrapper::checkException(StatusWrapper* status)
47{
48 if (status->dirty && (status->getState() & fb::IStatus::STATE_ERRORS))
49 throw DatabaseException{*status->client, status->getErrors()};
50}
51
52void StatusWrapper::catchException(fb::IStatus* status) noexcept
53{
54 assert(false);
55}
56
57
58std::string DatabaseException::buildMessage(Client& client, const std::intptr_t* statusVector)
59{
60 constexpr char DEFAULT_MESSAGE[] = "Unknown database error";
61
62 if (!statusVector)
63 return DEFAULT_MESSAGE;
64
65 const auto util = client.getUtil();
66
67 const auto status = client.newStatus();
68 status->setErrors(statusVector);
69
70 constexpr unsigned MAX_BUFFER_SIZE = 32u * 1024u;
71 unsigned bufferSize = 256u;
72 std::string message;
73
74 while (bufferSize <= MAX_BUFFER_SIZE)
75 {
76 std::string buffer(bufferSize, '\0');
77 const auto written = util->formatStatus(buffer.data(), bufferSize, status.get());
78
79 if (written < bufferSize && buffer[0] != '\0')
80 {
81 message = written == 0 ? std::string{buffer.c_str()} : std::string{buffer.data(), written};
82 break;
83 }
84
85 if (bufferSize == MAX_BUFFER_SIZE)
86 {
87 message = buffer.c_str();
88 break;
89 }
90
91 bufferSize = (bufferSize > MAX_BUFFER_SIZE / 2u) ? MAX_BUFFER_SIZE : bufferSize * 2u;
92 }
93
94 if (message.empty())
95 message = DEFAULT_MESSAGE;
96
97 return message;
98}
99
100void DatabaseException::copyErrorVector(const std::intptr_t* statusVector)
101{
102 if (!statusVector)
103 return;
104
105 const auto* p = statusVector;
106
107 while (*p != isc_arg_end)
108 {
109 const auto argType = *p++;
110
111 switch (argType)
112 {
113 case isc_arg_gds:
114 case isc_arg_number:
115 errorVector.push_back(argType);
116 errorVector.push_back(*p++);
117 break;
118
119 case isc_arg_string:
120 case isc_arg_interpreted:
121 case isc_arg_sql_state:
122 errorVector.push_back(argType);
123 errorStrings.emplace_back(reinterpret_cast<const char*>(*p++));
124 errorVector.push_back(0); // placeholder for string pointer
125 break;
126
127 case isc_arg_cstring:
128 {
129 const auto len = static_cast<size_t>(*p++);
130 const auto str = reinterpret_cast<const char*>(*p++);
131 errorVector.push_back(isc_arg_string);
132 errorStrings.emplace_back(str, len);
133 errorVector.push_back(0); // placeholder for string pointer
134 break;
135 }
136
137 default:
138 errorVector.push_back(argType);
139 errorVector.push_back(*p++);
140 break;
141 }
142 }
143
144 errorVector.push_back(isc_arg_end);
145
146 fixupStringPointers();
147}
148
149void DatabaseException::fixupStringPointers()
150{
151 size_t strIdx = 0;
152 size_t i = 0;
153
154 while (i < errorVector.size() && errorVector[i] != isc_arg_end)
155 {
156 const auto argType = errorVector[i];
157
158 if (argType == isc_arg_string || argType == isc_arg_interpreted || argType == isc_arg_sql_state)
159 errorVector[i + 1] = reinterpret_cast<std::intptr_t>(errorStrings[strIdx++].c_str());
160
161 i += 2;
162 }
163}
164
165std::string DatabaseException::extractSqlState(const std::intptr_t* statusVector)
166{
167 if (!statusVector)
168 return {};
169
170 const auto* p = statusVector;
171
172 while (*p != isc_arg_end)
173 {
174 const auto argType = *p++;
175
176 if (argType == isc_arg_sql_state)
177 return reinterpret_cast<const char*>(*p);
178
179 if (argType == isc_arg_cstring)
180 p += 2;
181 else
182 p++;
183 }
184
185 return {};
186}
Represents a Firebird client library instance.
Definition Client.h:53
fb::IUtil * getUtil()
Returns a Firebird IUtil interface.
Definition Client.h:144
FbUniquePtr< fb::IStatus > newStatus()
Creates and returns a Firebird IStatus instance.
Definition Client.h:199
Exception thrown when a Firebird database operation fails.
Definition Exception.h:247
const std::vector< std::intptr_t > & getErrors() const noexcept
Returns the Firebird error vector.
Definition Exception.h:277
fb-cpp namespace.
Definition Attachment.h:42