]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/ssqlite3.cc
Merge pull request #11431 from jroessler-ox/docs-kskzskroll-update
[thirdparty/pdns.git] / pdns / ssqlite3.cc
CommitLineData
fc3c07b4
PL
1/* SQLite backend for PowerDNS
2 * Copyright (C) 2003, Michel Stol <michel@powerdns.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2
6 * as published by the Free Software Foundation.
7 *
8 * Additionally, the license of this program contains a special
9 * exception which allows to distribute the program in binary form when
10 * it is linked against OpenSSL.
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 General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 */
0b67a76d 21
870a0fe4
AT
22#ifdef HAVE_CONFIG_H
23#include "config.h"
24#endif
0b67a76d 25#include <string>
e237ea04 26#include <sstream>
0b67a76d
BH
27#include "ssqlite3.hh"
28#include <iostream>
e237ea04 29#include <fstream>
7e5efe3c 30#include <utility>
e237ea04
PD
31#include "pdns/logger.hh"
32#include "misc.hh"
37079a8e 33#include "utility.hh"
76473b92 34#include <unistd.h>
0b67a76d 35
0f310932
AT
36/*
37** Set all the parameters in the compiled SQL statement to NULL.
38*
22cf81b0 39* copied from sqlite 3.3.6 // cmouse
0f310932 40*/
050e6877 41#if SQLITE_VERSION_NUMBER < 3003009
ca0b6be1
FM
42static int pdns_sqlite3_clear_bindings(sqlite3_stmt* pStmt)
43{
0f310932
AT
44 int i;
45 int rc = SQLITE_OK;
ca0b6be1 46 for (i = 1; rc == SQLITE_OK && i <= sqlite3_bind_parameter_count(pStmt); i++) {
0f310932
AT
47 rc = sqlite3_bind_null(pStmt, i);
48 }
49 return rc;
50}
050e6877 51#endif
0f310932 52
7e5efe3c 53static string SSQLite3ErrorString(sqlite3* database)
7919673a 54{
7e5efe3c 55 return string(sqlite3_errmsg(database) + string(" (") + std::to_string(sqlite3_extended_errcode(database)) + string(")"));
7919673a
PD
56}
57
ca0b6be1 58class SSQLite3Statement : public SSqlStatement
0f310932
AT
59{
60public:
7e5efe3c
FM
61 SSQLite3Statement(SSQLite3* database, bool dolog, string query) :
62 d_query(std::move(query)),
63 d_db(database),
eace2c24 64 d_dolog(dolog)
0f310932 65 {
0f310932
AT
66 }
67
7e5efe3c
FM
68 SSQLite3Statement(const SSQLite3Statement&) = delete;
69 SSQLite3Statement(SSQLite3Statement&&) = delete;
70 SSQLite3Statement& operator=(const SSQLite3Statement&) = delete;
71 SSQLite3Statement& operator=(SSQLite3Statement&&) = delete;
72
ca0b6be1
FM
73 int name2idx(const string& name)
74 {
75 string zName = string(":") + name;
488f9e9e 76 prepareStatement();
0f310932 77 return sqlite3_bind_parameter_index(d_stmt, zName.c_str());
22cf81b0 78 // XXX: support @ and $?
0f310932
AT
79 }
80
7e5efe3c 81 SSqlStatement* bind(const string& name, bool value) override
ca0b6be1
FM
82 {
83 int idx = name2idx(name);
84 if (idx > 0) {
85 sqlite3_bind_int(d_stmt, idx, value ? 1 : 0);
86 };
87 return this;
88 }
7e5efe3c
FM
89
90 SSqlStatement* bind(const string& name, int value) override
ca0b6be1
FM
91 {
92 int idx = name2idx(name);
93 if (idx > 0) {
94 sqlite3_bind_int(d_stmt, idx, value);
95 };
96 return this;
97 }
7e5efe3c
FM
98
99 SSqlStatement* bind(const string& name, uint32_t value) override
ca0b6be1
FM
100 {
101 int idx = name2idx(name);
102 if (idx > 0) {
103 sqlite3_bind_int64(d_stmt, idx, value);
104 };
105 return this;
106 }
7e5efe3c
FM
107
108 SSqlStatement* bind(const string& name, long value) override
ca0b6be1
FM
109 {
110 int idx = name2idx(name);
111 if (idx > 0) {
112 sqlite3_bind_int64(d_stmt, idx, value);
113 };
114 return this;
115 }
7e5efe3c
FM
116
117 SSqlStatement* bind(const string& name, unsigned long value) override
ca0b6be1
FM
118 {
119 int idx = name2idx(name);
120 if (idx > 0) {
7e5efe3c 121 sqlite3_bind_int64(d_stmt, idx, static_cast<sqlite3_int64>(value));
ca0b6be1
FM
122 };
123 return this;
124 }
7e5efe3c
FM
125
126 SSqlStatement* bind(const string& name, long long value) override
ca0b6be1
FM
127 {
128 int idx = name2idx(name);
129 if (idx > 0) {
130 sqlite3_bind_int64(d_stmt, idx, value);
131 };
132 return this;
133 };
7e5efe3c
FM
134
135 SSqlStatement* bind(const string& name, unsigned long long value) override
ca0b6be1
FM
136 {
137 int idx = name2idx(name);
138 if (idx > 0) {
7e5efe3c 139 sqlite3_bind_int64(d_stmt, idx, static_cast<sqlite3_int64>(value));
ca0b6be1
FM
140 };
141 return this;
142 }
7e5efe3c
FM
143
144 SSqlStatement* bind(const string& name, const std::string& value) override
ca0b6be1
FM
145 {
146 int idx = name2idx(name);
147 if (idx > 0) {
7e5efe3c 148 sqlite3_bind_text(d_stmt, idx, value.c_str(), static_cast<int>(value.size()), SQLITE_TRANSIENT);
ca0b6be1
FM
149 };
150 return this;
151 }
7e5efe3c
FM
152
153 SSqlStatement* bindNull(const string& name) override
ca0b6be1
FM
154 {
155 int idx = name2idx(name);
156 if (idx > 0) {
157 sqlite3_bind_null(d_stmt, idx);
158 };
159 return this;
160 }
0f310932 161
7e5efe3c 162 SSqlStatement* execute() override
ca0b6be1 163 {
488f9e9e 164 prepareStatement();
16c2f740 165 if (d_dolog) {
7e5efe3c 166 g_log << Logger::Warning << "Query " << this << ": " << d_query << endl;
16c2f740 167 d_dtime.set();
168 }
7e5efe3c
FM
169
170 int attempts = d_db->inTransaction() ? 1 : 0; // try only once
171 while (attempts < 2 && (d_rc = sqlite3_step(d_stmt)) == SQLITE_BUSY) {
ca0b6be1 172 attempts++;
7e5efe3c 173 }
0f310932
AT
174
175 if (d_rc != SQLITE_ROW && d_rc != SQLITE_DONE) {
176 // failed.
488f9e9e 177 releaseStatement();
7e5efe3c 178 if (d_rc == SQLITE_CANTOPEN) {
ca0b6be1 179 throw SSqlException(string("CANTOPEN error in sqlite3, often caused by unwritable sqlite3 db *directory*: ") + SSQLite3ErrorString(d_db->db()));
7e5efe3c 180 }
ca0b6be1 181 throw SSqlException(string("Error while retrieving SQLite query results: ") + SSQLite3ErrorString(d_db->db()));
0f310932 182 }
7e5efe3c
FM
183 if (d_dolog) {
184 g_log << Logger::Warning << "Query " << this << ": " << d_dtime.udiffNoReset() << " us to execute" << endl;
185 }
0f310932
AT
186 return this;
187 }
7e5efe3c
FM
188
189 bool hasNextRow() override
ca0b6be1
FM
190 {
191 if (d_dolog && d_rc != SQLITE_ROW) {
7e5efe3c 192 g_log << Logger::Warning << "Query " << this << ": " << d_dtime.udiffNoReset() << " us total to last row" << endl;
16c2f740 193 }
194 return d_rc == SQLITE_ROW;
195 }
0f310932 196
7e5efe3c 197 SSqlStatement* nextRow(row_t& row) override
ca0b6be1 198 {
0f310932
AT
199 row.clear();
200 int numCols = sqlite3_column_count(d_stmt);
201 row.reserve(numCols); // preallocate memory
202 // Another row received, process it.
ca0b6be1
FM
203 for (int i = 0; i < numCols; i++) {
204 if (sqlite3_column_type(d_stmt, i) == SQLITE_NULL) {
2ea3d25e 205 row.emplace_back("");
ca0b6be1
FM
206 }
207 else {
7e5efe3c 208 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast): SQLite API returns unsigned char strings and std::strings are signed char.
ca0b6be1 209 const char* pData = (const char*)sqlite3_column_text(d_stmt, i);
2ea3d25e 210 row.emplace_back(pData, sqlite3_column_bytes(d_stmt, i));
0f310932
AT
211 }
212 }
213 d_rc = sqlite3_step(d_stmt);
214 return this;
215 }
216
7e5efe3c 217 SSqlStatement* getResult(result_t& result) override
ca0b6be1 218 {
0f310932 219 result.clear();
ca0b6be1 220 while (hasNextRow()) {
0f310932
AT
221 row_t row;
222 nextRow(row);
2ea3d25e 223 result.push_back(std::move(row));
0f310932
AT
224 }
225 return this;
226 }
227
7e5efe3c 228 SSqlStatement* reset() override
ca0b6be1 229 {
0f310932
AT
230 sqlite3_reset(d_stmt);
231#if SQLITE_VERSION_NUMBER >= 3003009
232 sqlite3_clear_bindings(d_stmt);
233#else
234 pdns_sqlite3_clear_bindings(d_stmt);
235#endif
236 return this;
237 }
238
7e5efe3c 239 ~SSQLite3Statement() override
ca0b6be1 240 {
0f310932 241 // deallocate if necessary
488f9e9e 242 releaseStatement();
0f310932
AT
243 }
244
7e5efe3c 245 const string& getQuery() override { return d_query; };
ca0b6be1 246
0f310932
AT
247private:
248 string d_query;
16c2f740 249 DTime d_dtime;
eace2c24
RG
250 sqlite3_stmt* d_stmt{nullptr};
251 SSQLite3* d_db{nullptr};
252 int d_rc{0};
253 bool d_dolog;
254 bool d_prepared{false};
255
ca0b6be1
FM
256 void prepareStatement()
257 {
7e5efe3c 258 const char* pTail = nullptr;
488f9e9e 259
7e5efe3c 260 if (d_prepared) {
ca0b6be1 261 return;
7e5efe3c 262 }
488f9e9e 263#if SQLITE_VERSION_NUMBER >= 3003009
ca0b6be1 264 if (sqlite3_prepare_v2(d_db->db(), d_query.c_str(), -1, &d_stmt, &pTail) != SQLITE_OK)
488f9e9e 265#else
ca0b6be1 266 if (sqlite3_prepare(d_db->db(), d_query.c_str(), -1, &d_stmt, &pTail) != SQLITE_OK)
488f9e9e
AT
267#endif
268 {
269 releaseStatement();
ca0b6be1 270 throw SSqlException(string("Unable to compile SQLite statement : '") + d_query + "': " + SSQLite3ErrorString(d_db->db()));
488f9e9e 271 }
7e5efe3c 272 if ((pTail != nullptr) && strlen(pTail) > 0) {
ca0b6be1 273 g_log << Logger::Warning << "Sqlite3 command partially processed. Unprocessed part: " << pTail << endl;
7e5efe3c 274 }
488f9e9e
AT
275 d_prepared = true;
276 }
277
ca0b6be1
FM
278 void releaseStatement()
279 {
7e5efe3c 280 if (d_stmt != nullptr) {
488f9e9e 281 sqlite3_finalize(d_stmt);
7e5efe3c 282 }
eace2c24 283 d_stmt = nullptr;
488f9e9e
AT
284 d_prepared = false;
285 }
0f310932
AT
286};
287
0b67a76d 288// Constructor.
ca0b6be1 289SSQLite3::SSQLite3(const std::string& database, const std::string& journalmode, bool creat)
0b67a76d 290{
ca0b6be1 291 if (access(database.c_str(), F_OK) == -1) {
7e5efe3c 292 if (!creat) {
b81ad966 293 throw SSqlException("SQLite database '" + database + "' does not exist yet");
7e5efe3c 294 }
ca0b6be1
FM
295 }
296 else {
7e5efe3c 297 if (creat) {
b81ad966 298 throw SSqlException("SQLite database '" + database + "' already exists");
7e5efe3c 299 }
b6068fce 300 }
0b67a76d 301
7e5efe3c 302 if (sqlite3_open(database.c_str(), &m_pDB) != SQLITE_OK) {
b81ad966 303 throw SSqlException("Could not connect to the SQLite database '" + database + "'");
7e5efe3c
FM
304 }
305 m_dolog = false;
0f310932 306 m_in_transaction = false;
7e5efe3c 307 sqlite3_busy_handler(m_pDB, busyHandler, nullptr);
367f9b40 308
7e5efe3c 309 if (journalmode.length() != 0) {
b81ad966 310 executeImpl("PRAGMA journal_mode=" + journalmode);
7e5efe3c 311 }
0b67a76d
BH
312}
313
e237ea04 314void SSQLite3::setLog(bool state)
22cf81b0 315{
ca0b6be1 316 m_dolog = state;
e237ea04
PD
317}
318
0b67a76d 319// Destructor.
6edbdc95 320SSQLite3::~SSQLite3()
0b67a76d 321{
7e5efe3c
FM
322 for (int tries = 0;; ++tries) {
323 int ret = sqlite3_close(m_pDB);
324 if (ret != SQLITE_OK) {
325 if (tries != 0 || ret != SQLITE_BUSY) { // if we have SQLITE_BUSY, and a working m_Pstmt, try finalize
ca0b6be1 326 cerr << "Unable to close down sqlite connection: " << ret << endl;
0ba2a32b
BH
327 abort();
328 }
0ba2a32b 329 }
7e5efe3c 330 else {
0ba2a32b 331 break;
7e5efe3c 332 }
6edbdc95 333 }
0b67a76d
BH
334}
335
ca0b6be1
FM
336std::unique_ptr<SSqlStatement> SSQLite3::prepare(const string& query, int nparams __attribute__((unused)))
337{
2bbc9eb0 338 return std::make_unique<SSQLite3Statement>(this, m_dolog, query);
0b67a76d
BH
339}
340
b81ad966 341void SSQLite3::executeImpl(const string& query)
ca0b6be1 342{
7e5efe3c 343 char* errmsg = nullptr;
ca6b759c 344 std::string errstr1;
7e5efe3c
FM
345 int execRet = sqlite3_exec(m_pDB, query.c_str(), nullptr, nullptr, &errmsg);
346 if (execRet != SQLITE_OK) {
ca6b759c 347 errstr1 = errmsg;
e54d78b9 348 sqlite3_free(errmsg);
ca6b759c 349 }
7e5efe3c 350 if (execRet == SQLITE_BUSY) {
0f310932 351 if (m_in_transaction) {
a5c2ec49 352 throw SSqlException("Failed to execute query: " + errstr1);
ca0b6be1 353 }
7e5efe3c
FM
354 execRet = sqlite3_exec(m_pDB, query.c_str(), nullptr, nullptr, &errmsg);
355 std::string errstr2;
356 if (execRet != SQLITE_OK) {
357 errstr2 = errmsg;
358 sqlite3_free(errmsg);
359 throw SSqlException("Failed to execute query: " + errstr2);
0f310932 360 }
ca0b6be1 361 }
7e5efe3c 362 else if (execRet != SQLITE_OK) {
4482ac79 363 throw SSqlException("Failed to execute query: " + errstr1);
0f310932 364 }
0b67a76d
BH
365}
366
b81ad966
FM
367void SSQLite3::execute(const string& query)
368{
369 executeImpl(query);
370}
371
7e5efe3c 372int SSQLite3::busyHandler([[maybe_unused]] void* userData, [[maybe_unused]] int invocationsSoFar)
cb8934ae 373{
de8ce191 374 Utility::usleep(1000);
cb8934ae
BH
375 return 1;
376}
0b67a76d 377
ca0b6be1
FM
378void SSQLite3::startTransaction()
379{
0f310932
AT
380 execute("begin");
381 m_in_transaction = true;
382}
0b67a76d 383
ca0b6be1
FM
384void SSQLite3::rollback()
385{
0f310932
AT
386 execute("rollback");
387 m_in_transaction = false;
0b67a76d
BH
388}
389
ca0b6be1
FM
390void SSQLite3::commit()
391{
0f310932
AT
392 execute("commit");
393 m_in_transaction = false;
394}
0b67a76d 395
0f310932 396// Constructs a SSqlException object.
ca0b6be1 397SSqlException SSQLite3::sPerrorException(const std::string& reason)
0b67a76d 398{
7e5efe3c 399 return {reason};
0b67a76d 400}