SNode.C
Loading...
Searching...
No Matches
MariaDBConnection.cpp
Go to the documentation of this file.
1/*
2 * SNode.C - A Slim Toolkit for Network Communication
3 * Copyright (C) Volker Christian <me@vchrist.at>
4 * 2020, 2021, 2022, 2023, 2024, 2025
5 * 2021, 2022 Daniel Flockert
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published
9 * by the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21/*
22 * MIT License
23 *
24 * Permission is hereby granted, free of charge, to any person obtaining a copy
25 * of this software and associated documentation files (the "Software"), to deal
26 * in the Software without restriction, including without limitation the rights
27 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28 * copies of the Software, and to permit persons to whom the Software is
29 * furnished to do so, subject to the following conditions:
30 *
31 * The above copyright notice and this permission notice shall be included in
32 * all copies or substantial portions of the Software.
33 *
34 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
37 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
40 * THE SOFTWARE.
41 */
42
43#include "database/mariadb/MariaDBConnection.h"
44
45#include "database/mariadb/MariaDBClient.h"
46#include "database/mariadb/commands/async/MariaDBConnectCommand.h"
47
48#ifndef DOXYGEN_SHOULD_SKIP_THIS
49
50#include "core/SNodeC.h"
51#include "log/Logger.h"
52#include "utils/Timeval.h"
53
54#include <mysql.h>
55#include <string>
56#include <utility>
57
58#endif /* DOXYGEN_SHOULD_SKIP_THIS */
59
60namespace database::mariadb {
61
62 MariaDBConnection::MariaDBConnection(MariaDBClient* mariaDBClient, const MariaDBConnectionDetails& connectionDetails)
63 : ReadEventReceiver("MariaDBConnectionRead", core::DescriptorEventReceiver::TIMEOUT::DISABLE)
64 , WriteEventReceiver("MariaDBConnectionWrite", core::DescriptorEventReceiver::TIMEOUT::DISABLE)
65 , ExceptionalConditionEventReceiver("MariaDBConnectionExceptional", core::DescriptorEventReceiver::TIMEOUT::DISABLE)
66 , mariaDBClient(mariaDBClient)
67 , mysql(mysql_init(nullptr))
68 , commandStartEvent("MariaDBCommandStartEvent", this) {
69 mysql_options(mysql, MYSQL_OPT_NONBLOCK, nullptr);
70
71 execute_async(std::move(MariaDBCommandSequence().execute_async(new database::mariadb::commands::async::MariaDBConnectCommand(
72 connectionDetails,
73 [this]() {
74 if (mysql_errno(mysql) == 0) {
75 const int fd = mysql_get_socket(mysql);
76
77 LOG(DEBUG) << "MariaDB: Got valid descriptor: " << fd;
78
79 if (ReadEventReceiver::enable(fd) && WriteEventReceiver::enable(fd) && ExceptionalConditionEventReceiver::enable(fd)) {
80 ReadEventReceiver::suspend();
81 WriteEventReceiver::suspend();
82 ExceptionalConditionEventReceiver::suspend();
83
84 connected = true;
85 } else {
86 if (ReadEventReceiver::isEnabled()) {
87 ReadEventReceiver::disable();
88 }
89 if (WriteEventReceiver::isEnabled()) {
90 WriteEventReceiver::disable();
91 }
92 if (ExceptionalConditionEventReceiver::isEnabled()) {
93 ExceptionalConditionEventReceiver::disable();
94 }
95 }
96 } else {
97 LOG(WARNING) << "MariaDB: Got no valid descriptor: " << mysql_error(mysql) << ", " << mysql_errno(mysql);
98 }
99 },
100 []() {
101 LOG(DEBUG) << "MariaDB: Connect success";
102 },
103 [](const std::string& errorString, unsigned int errorNumber) {
104 LOG(WARNING) << "MariaDB: Connect error: " << errorString << " : " << errorNumber;
105 }))));
106 }
107
108 MariaDBConnection::~MariaDBConnection() {
109 for (MariaDBCommandSequence& mariaDBCommandSequence : commandSequenceQueue) {
110 for (MariaDBCommand* mariaDBCommand : mariaDBCommandSequence.sequence()) {
111 if (core::SNodeC::state() == core::State::RUNNING && connected) {
112 mariaDBCommand->commandError(mysql_error(mysql), mysql_errno(mysql));
113 }
114
115 delete mariaDBCommand;
116 }
117 }
118
119 if (mariaDBClient != nullptr) {
120 mariaDBClient->connectionVanished();
121 }
122
123 mysql_close(mysql);
124 mysql_library_end();
125 }
126
127 MariaDBCommandSequence& MariaDBConnection::execute_async(MariaDBCommandSequence&& commandSequence) {
128 if (currentCommand == nullptr && commandSequenceQueue.empty()) {
129 commandStartEvent.span();
130 }
131
132 commandSequenceQueue.emplace_back(std::move(commandSequence));
133
134 return commandSequenceQueue.back();
135 }
136
137 void MariaDBConnection::execute_sync(MariaDBCommand* mariaDBCommand) {
138 mariaDBCommand->commandStart(mysql, utils::Timeval::currentTime());
139
140 if (mysql_errno(mysql) == 0) {
141 if (mariaDBCommand->commandCompleted()) {
142 }
143 } else {
144 mariaDBCommand->commandError(mysql_error(mysql), mysql_errno(mysql));
145 }
146
147 delete mariaDBCommand;
148 }
149
150 void MariaDBConnection::commandStart(const utils::Timeval& currentTime) {
151 if (!commandSequenceQueue.empty()) {
152 currentCommand = commandSequenceQueue.front().nextCommand();
153
154 LOG(DEBUG) << "MariaDB: Start: " << currentCommand->commandInfo();
155
156 currentCommand->setMariaDBConnection(this);
157 checkStatus(currentCommand->commandStart(mysql, currentTime));
158 } else if (mariaDBClient != nullptr) {
159 if (ReadEventReceiver::isSuspended() && ReadEventReceiver::isEnabled()) {
160 ReadEventReceiver::resume();
161 }
162 } else {
163 ReadEventReceiver::disable();
164 WriteEventReceiver::disable();
165 ExceptionalConditionEventReceiver::disable();
166 }
167 }
168
169 void MariaDBConnection::commandContinue(int status) {
170 if (currentCommand != nullptr) {
171 checkStatus(currentCommand->commandContinue(status));
172 } else if ((status & MYSQL_WAIT_READ) != 0 && commandSequenceQueue.empty()) {
173 ReadEventReceiver::disable();
174 WriteEventReceiver::disable();
175 ExceptionalConditionEventReceiver::disable();
176 }
177 }
178
179 void MariaDBConnection::commandCompleted() {
180 LOG(DEBUG) << "MariaDB: Completed: " << currentCommand->commandInfo();
181 commandSequenceQueue.front().commandCompleted();
182
183 if (commandSequenceQueue.front().empty()) {
184 commandSequenceQueue.pop_front();
185 }
186
187 delete currentCommand;
188 currentCommand = nullptr;
189 }
190
191 void MariaDBConnection::unmanaged() {
192 mariaDBClient = nullptr;
193 }
194
195 void MariaDBConnection::checkStatus(int status) {
196 if (connected) {
197 if ((status & MYSQL_WAIT_READ) != 0) {
198 if (ReadEventReceiver::isSuspended() && ReadEventReceiver::isEnabled()) {
199 ReadEventReceiver::resume();
200 }
201 } else if (!ReadEventReceiver::isSuspended()) {
202 ReadEventReceiver::suspend();
203 }
204
205 if ((status & MYSQL_WAIT_WRITE) != 0) {
206 if (WriteEventReceiver::isSuspended() && WriteEventReceiver::isEnabled()) {
207 WriteEventReceiver::resume();
208 }
209 } else if (!WriteEventReceiver::isSuspended()) {
210 WriteEventReceiver::suspend();
211 }
212
213 if ((status & MYSQL_WAIT_EXCEPT) != 0) {
214 if (ExceptionalConditionEventReceiver::isSuspended() && ExceptionalConditionEventReceiver::isEnabled()) {
215 ExceptionalConditionEventReceiver::resume();
216 }
217 } else if (!ExceptionalConditionEventReceiver::isSuspended()) {
218 ExceptionalConditionEventReceiver::suspend();
219 }
220
221 if ((status & MYSQL_WAIT_TIMEOUT) != 0) {
222 ReadEventReceiver::setTimeout(mysql_get_timeout_value(mysql));
223 // WriteEventReceiver::setTimeout(mysql_get_timeout_value(mysql));
224 // ExceptionalConditionEventReceiver::setTimeout(mysql_get_timeout_value(mysql));
225 } else {
226 ReadEventReceiver::setTimeout(core::DescriptorEventReceiver::TIMEOUT::DEFAULT);
227 // WriteEventReceiver::setTimeout(core::DescriptorEventReceiver::TIMEOUT::DEFAULT);
228 // ExceptionalConditionEventReceiver::setTimeout(core::DescriptorEventReceiver::TIMEOUT::DEFAULT);
229 }
230
231 if (status == 0) {
232 if (mysql_errno(mysql) == 0) {
233 if (currentCommand->commandCompleted()) {
234 commandCompleted();
235 }
236 } else {
237 currentCommand->commandError(mysql_error(mysql), mysql_errno(mysql));
238 commandCompleted();
239 }
240 commandStartEvent.span();
241 }
242 } else {
243 currentCommand->commandError(mysql_error(mysql), mysql_errno(mysql));
244 commandCompleted();
245 delete this;
246 }
247 }
248
249 void MariaDBConnection::readEvent() {
250 commandContinue(MYSQL_WAIT_READ);
251 }
252
253 void MariaDBConnection::writeEvent() {
254 commandContinue(MYSQL_WAIT_WRITE);
255 }
256
257 void MariaDBConnection::outOfBandEvent() {
258 commandContinue(MYSQL_WAIT_EXCEPT);
259 }
260
261 void MariaDBConnection::readTimeout() {
262 commandContinue(MYSQL_WAIT_TIMEOUT);
263 }
264
265 void MariaDBConnection::writeTimeout() {
266 commandContinue(MYSQL_WAIT_TIMEOUT);
267 }
268
269 void MariaDBConnection::outOfBandTimeout() {
270 commandContinue(MYSQL_WAIT_TIMEOUT);
271 }
272
273 void MariaDBConnection::unobservedEvent() {
274 delete this;
275 }
276
277 MariaDBCommandStartEvent::MariaDBCommandStartEvent(const std::string& name, MariaDBConnection* mariaDBConnection)
278 : core::EventReceiver(name)
279 , mariaDBConnection(mariaDBConnection) {
280 }
281
282 void MariaDBCommandStartEvent::onEvent(const utils::Timeval& currentTime) {
283 mariaDBConnection->commandStart(currentTime);
284 }
285
286 void MariaDBCommandStartEvent::destruct() {
287 delete mariaDBConnection;
288 }
289
290} // namespace database::mariadb