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#include "database/mariadb/MariaDBConnection.h"
22
23#include "database/mariadb/MariaDBClient.h"
24#include "database/mariadb/commands/async/MariaDBConnectCommand.h"
25
26#ifndef DOXYGEN_SHOULD_SKIP_THIS
27
28#include "core/SNodeC.h"
29#include "log/Logger.h"
30#include "utils/Timeval.h"
31
32#include <mysql.h>
33#include <string>
34#include <utility>
35
36#endif /* DOXYGEN_SHOULD_SKIP_THIS */
37
38namespace database::mariadb {
39
40 MariaDBConnection::MariaDBConnection(MariaDBClient* mariaDBClient, const MariaDBConnectionDetails& connectionDetails)
41 : ReadEventReceiver("MariaDBConnectionRead", core::DescriptorEventReceiver::TIMEOUT::DISABLE)
42 , WriteEventReceiver("MariaDBConnectionWrite", core::DescriptorEventReceiver::TIMEOUT::DISABLE)
43 , ExceptionalConditionEventReceiver("MariaDBConnectionExceptional", core::DescriptorEventReceiver::TIMEOUT::DISABLE)
44 , mariaDBClient(mariaDBClient)
45 , mysql(mysql_init(nullptr))
46 , commandStartEvent("MariaDBCommandStartEvent", this) {
47 mysql_options(mysql, MYSQL_OPT_NONBLOCK, nullptr);
48
49 execute_async(std::move(MariaDBCommandSequence().execute_async(new database::mariadb::commands::async::MariaDBConnectCommand(
50 connectionDetails,
51 [this]() {
52 if (mysql_errno(mysql) == 0) {
53 const int fd = mysql_get_socket(mysql);
54
55 LOG(DEBUG) << "MariaDB: Got valid descriptor: " << fd;
56
57 if (ReadEventReceiver::enable(fd) && WriteEventReceiver::enable(fd) && ExceptionalConditionEventReceiver::enable(fd)) {
58 ReadEventReceiver::suspend();
59 WriteEventReceiver::suspend();
60 ExceptionalConditionEventReceiver::suspend();
61
62 connected = true;
63 } else {
64 if (ReadEventReceiver::isEnabled()) {
65 ReadEventReceiver::disable();
66 }
67 if (WriteEventReceiver::isEnabled()) {
68 WriteEventReceiver::disable();
69 }
70 if (ExceptionalConditionEventReceiver::isEnabled()) {
71 ExceptionalConditionEventReceiver::disable();
72 }
73 }
74 } else {
75 LOG(WARNING) << "MariaDB: Got no valid descriptor: " << mysql_error(mysql) << ", " << mysql_errno(mysql);
76 }
77 },
78 []() {
79 LOG(DEBUG) << "MariaDB: Connect success";
80 },
81 [](const std::string& errorString, unsigned int errorNumber) {
82 LOG(WARNING) << "MariaDB: Connect error: " << errorString << " : " << errorNumber;
83 }))));
84 }
85
86 MariaDBConnection::~MariaDBConnection() {
87 for (MariaDBCommandSequence& mariaDBCommandSequence : commandSequenceQueue) {
88 for (MariaDBCommand* mariaDBCommand : mariaDBCommandSequence.sequence()) {
89 if (core::SNodeC::state() == core::State::RUNNING && connected) {
90 mariaDBCommand->commandError(mysql_error(mysql), mysql_errno(mysql));
91 }
92
93 delete mariaDBCommand;
94 }
95 }
96
97 if (mariaDBClient != nullptr) {
98 mariaDBClient->connectionVanished();
99 }
100
101 mysql_close(mysql);
102 mysql_library_end();
103 }
104
105 MariaDBCommandSequence& MariaDBConnection::execute_async(MariaDBCommandSequence&& commandSequence) {
106 if (currentCommand == nullptr && commandSequenceQueue.empty()) {
107 commandStartEvent.span();
108 }
109
110 commandSequenceQueue.emplace_back(std::move(commandSequence));
111
112 return commandSequenceQueue.back();
113 }
114
115 void MariaDBConnection::execute_sync(MariaDBCommand* mariaDBCommand) {
116 mariaDBCommand->commandStart(mysql, utils::Timeval::currentTime());
117
118 if (mysql_errno(mysql) == 0) {
119 if (mariaDBCommand->commandCompleted()) {
120 }
121 } else {
122 mariaDBCommand->commandError(mysql_error(mysql), mysql_errno(mysql));
123 }
124
125 delete mariaDBCommand;
126 }
127
128 void MariaDBConnection::commandStart(const utils::Timeval& currentTime) {
129 if (!commandSequenceQueue.empty()) {
130 currentCommand = commandSequenceQueue.front().nextCommand();
131
132 LOG(DEBUG) << "MariaDB: Start: " << currentCommand->commandInfo();
133
134 currentCommand->setMariaDBConnection(this);
135 checkStatus(currentCommand->commandStart(mysql, currentTime));
136 } else if (mariaDBClient != nullptr) {
137 if (ReadEventReceiver::isSuspended() && ReadEventReceiver::isEnabled()) {
138 ReadEventReceiver::resume();
139 }
140 } else {
141 ReadEventReceiver::disable();
142 WriteEventReceiver::disable();
143 ExceptionalConditionEventReceiver::disable();
144 }
145 }
146
147 void MariaDBConnection::commandContinue(int status) {
148 if (currentCommand != nullptr) {
149 checkStatus(currentCommand->commandContinue(status));
150 } else if ((status & MYSQL_WAIT_READ) != 0 && commandSequenceQueue.empty()) {
151 ReadEventReceiver::disable();
152 WriteEventReceiver::disable();
153 ExceptionalConditionEventReceiver::disable();
154 }
155 }
156
157 void MariaDBConnection::commandCompleted() {
158 LOG(DEBUG) << "MariaDB: Completed: " << currentCommand->commandInfo();
159 commandSequenceQueue.front().commandCompleted();
160
161 if (commandSequenceQueue.front().empty()) {
162 commandSequenceQueue.pop_front();
163 }
164
165 delete currentCommand;
166 currentCommand = nullptr;
167 }
168
169 void MariaDBConnection::unmanaged() {
170 mariaDBClient = nullptr;
171 }
172
173 void MariaDBConnection::checkStatus(int status) {
174 if (connected) {
175 if ((status & MYSQL_WAIT_READ) != 0) {
176 if (ReadEventReceiver::isSuspended() && ReadEventReceiver::isEnabled()) {
177 ReadEventReceiver::resume();
178 }
179 } else if (!ReadEventReceiver::isSuspended()) {
180 ReadEventReceiver::suspend();
181 }
182
183 if ((status & MYSQL_WAIT_WRITE) != 0) {
184 if (WriteEventReceiver::isSuspended() && WriteEventReceiver::isEnabled()) {
185 WriteEventReceiver::resume();
186 }
187 } else if (!WriteEventReceiver::isSuspended()) {
188 WriteEventReceiver::suspend();
189 }
190
191 if ((status & MYSQL_WAIT_EXCEPT) != 0) {
192 if (ExceptionalConditionEventReceiver::isSuspended() && ExceptionalConditionEventReceiver::isEnabled()) {
193 ExceptionalConditionEventReceiver::resume();
194 }
195 } else if (!ExceptionalConditionEventReceiver::isSuspended()) {
196 ExceptionalConditionEventReceiver::suspend();
197 }
198
199 if ((status & MYSQL_WAIT_TIMEOUT) != 0) {
200 ReadEventReceiver::setTimeout(mysql_get_timeout_value(mysql));
201 // WriteEventReceiver::setTimeout(mysql_get_timeout_value(mysql));
202 // ExceptionalConditionEventReceiver::setTimeout(mysql_get_timeout_value(mysql));
203 } else {
204 ReadEventReceiver::setTimeout(core::DescriptorEventReceiver::TIMEOUT::DEFAULT);
205 // WriteEventReceiver::setTimeout(core::DescriptorEventReceiver::TIMEOUT::DEFAULT);
206 // ExceptionalConditionEventReceiver::setTimeout(core::DescriptorEventReceiver::TIMEOUT::DEFAULT);
207 }
208
209 if (status == 0) {
210 if (mysql_errno(mysql) == 0) {
211 if (currentCommand->commandCompleted()) {
212 commandCompleted();
213 }
214 } else {
215 currentCommand->commandError(mysql_error(mysql), mysql_errno(mysql));
216 commandCompleted();
217 }
218 commandStartEvent.span();
219 }
220 } else {
221 currentCommand->commandError(mysql_error(mysql), mysql_errno(mysql));
222 commandCompleted();
223 delete this;
224 }
225 }
226
227 void MariaDBConnection::readEvent() {
228 commandContinue(MYSQL_WAIT_READ);
229 }
230
231 void MariaDBConnection::writeEvent() {
232 commandContinue(MYSQL_WAIT_WRITE);
233 }
234
235 void MariaDBConnection::outOfBandEvent() {
236 commandContinue(MYSQL_WAIT_EXCEPT);
237 }
238
239 void MariaDBConnection::readTimeout() {
240 commandContinue(MYSQL_WAIT_TIMEOUT);
241 }
242
243 void MariaDBConnection::writeTimeout() {
244 commandContinue(MYSQL_WAIT_TIMEOUT);
245 }
246
247 void MariaDBConnection::outOfBandTimeout() {
248 commandContinue(MYSQL_WAIT_TIMEOUT);
249 }
250
251 void MariaDBConnection::unobservedEvent() {
252 delete this;
253 }
254
255 MariaDBCommandStartEvent::MariaDBCommandStartEvent(const std::string& name, MariaDBConnection* mariaDBConnection)
256 : core::EventReceiver(name)
257 , mariaDBConnection(mariaDBConnection) {
258 }
259
260 void MariaDBCommandStartEvent::onEvent(const utils::Timeval& currentTime) {
261 mariaDBConnection->commandStart(currentTime);
262 }
263
264 void MariaDBCommandStartEvent::destruct() {
265 delete mariaDBConnection;
266 }
267
268} // namespace database::mariadb