]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/ssqlite3.cc
Merge pull request #9070 from rgacogne/boost-173
[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
PD
29#include <fstream>
30#include "pdns/logger.hh"
31#include "misc.hh"
37079a8e 32#include "utility.hh"
76473b92 33#include <unistd.h>
0b67a76d 34
0f310932
AT
35/*
36** Set all the parameters in the compiled SQL statement to NULL.
37*
22cf81b0 38* copied from sqlite 3.3.6 // cmouse
0f310932 39*/
050e6877
RG
40#if SQLITE_VERSION_NUMBER < 3003009
41static int pdns_sqlite3_clear_bindings(sqlite3_stmt *pStmt){
0f310932
AT
42 int i;
43 int rc = SQLITE_OK;
44 for(i=1; rc==SQLITE_OK && i<=sqlite3_bind_parameter_count(pStmt); i++){
45 rc = sqlite3_bind_null(pStmt, i);
46 }
47 return rc;
48}
050e6877 49#endif
0f310932 50
7919673a
PD
51static string SSQLite3ErrorString(sqlite3 *db)
52{
53 return string(sqlite3_errmsg(db)+string(" (")+std::to_string(sqlite3_extended_errcode(db))+string(")"));
54}
55
22cf81b0 56class SSQLite3Statement: public SSqlStatement
0f310932
AT
57{
58public:
8a70e507 59 SSQLite3Statement(SSQLite3 *db, bool dolog, const string& query) :
8a70e507 60 d_query(query),
eace2c24
RG
61 d_db(db),
62 d_dolog(dolog)
0f310932 63 {
0f310932
AT
64 }
65
66 int name2idx(const string& name) {
67 string zName = string(":")+name;
488f9e9e 68 prepareStatement();
0f310932 69 return sqlite3_bind_parameter_index(d_stmt, zName.c_str());
22cf81b0 70 // XXX: support @ and $?
0f310932
AT
71 }
72
73 SSqlStatement* bind(const string& name, bool value) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_int(d_stmt, idx, value ? 1 : 0); }; return this; }
74 SSqlStatement* bind(const string& name, int value) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_int(d_stmt, idx, value); }; return this; }
75 SSqlStatement* bind(const string& name, uint32_t value) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_int64(d_stmt, idx, value); }; return this; }
76 SSqlStatement* bind(const string& name, long value) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_int64(d_stmt, idx, value); }; return this; }
77 SSqlStatement* bind(const string& name, unsigned long value) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_int64(d_stmt, idx, value); }; return this; }
78 SSqlStatement* bind(const string& name, long long value) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_int64(d_stmt, idx, value); }; return this; };
79 SSqlStatement* bind(const string& name, unsigned long long value) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_int64(d_stmt, idx, value); }; return this; }
80 SSqlStatement* bind(const string& name, const std::string& value) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_text(d_stmt, idx, value.c_str(), value.size(), SQLITE_TRANSIENT); }; return this; }
81 SSqlStatement* bindNull(const string& name) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_null(d_stmt, idx); }; return this; }
82
83 SSqlStatement* execute() {
488f9e9e 84 prepareStatement();
16c2f740 85 if (d_dolog) {
86 g_log<<Logger::Warning<< "Query "<<((long)(void*)this)<<": " << d_query << endl;
87 d_dtime.set();
88 }
0f310932
AT
89 int attempts = d_db->inTransaction(); // try only once
90 while(attempts < 2 && (d_rc = sqlite3_step(d_stmt)) == SQLITE_BUSY) attempts++;
91
92 if (d_rc != SQLITE_ROW && d_rc != SQLITE_DONE) {
93 // failed.
488f9e9e
AT
94 releaseStatement();
95 if (d_rc == SQLITE_CANTOPEN)
7919673a
PD
96 throw SSqlException(string("CANTOPEN error in sqlite3, often caused by unwritable sqlite3 db *directory*: ")+SSQLite3ErrorString(d_db->db()));
97 throw SSqlException(string("Error while retrieving SQLite query results: ")+SSQLite3ErrorString(d_db->db()));
0f310932 98 }
16c2f740 99 if(d_dolog)
100 g_log<<Logger::Warning<< "Query "<<((long)(void*)this)<<": "<<d_dtime.udiffNoReset()<<" usec to execute"<<endl;
0f310932
AT
101 return this;
102 }
16c2f740 103 bool hasNextRow() {
104 if(d_dolog && d_rc != SQLITE_ROW) {
105 g_log<<Logger::Warning<< "Query "<<((long)(void*)this)<<": "<<d_dtime.udiffNoReset()<<" total usec to last row"<<endl;
106 }
107 return d_rc == SQLITE_ROW;
108 }
0f310932
AT
109
110 SSqlStatement* nextRow(row_t& row) {
111 row.clear();
112 int numCols = sqlite3_column_count(d_stmt);
113 row.reserve(numCols); // preallocate memory
114 // Another row received, process it.
115 for ( int i=0; i<numCols; i++)
116 {
117 if (sqlite3_column_type(d_stmt,i) == SQLITE_NULL) {
2ea3d25e 118 row.emplace_back("");
0f310932
AT
119 } else {
120 const char *pData = (const char*) sqlite3_column_text(d_stmt, i);
2ea3d25e 121 row.emplace_back(pData, sqlite3_column_bytes(d_stmt, i));
0f310932
AT
122 }
123 }
124 d_rc = sqlite3_step(d_stmt);
125 return this;
126 }
127
128 SSqlStatement* getResult(result_t& result) {
129 result.clear();
130 while(hasNextRow()) {
131 row_t row;
132 nextRow(row);
2ea3d25e 133 result.push_back(std::move(row));
0f310932
AT
134 }
135 return this;
136 }
137
138 SSqlStatement* reset() {
139 sqlite3_reset(d_stmt);
140#if SQLITE_VERSION_NUMBER >= 3003009
141 sqlite3_clear_bindings(d_stmt);
142#else
143 pdns_sqlite3_clear_bindings(d_stmt);
144#endif
145 return this;
146 }
147
148 ~SSQLite3Statement() {
149 // deallocate if necessary
488f9e9e 150 releaseStatement();
0f310932
AT
151 }
152
153 const string& getQuery() { return d_query; };
154private:
155 string d_query;
16c2f740 156 DTime d_dtime;
eace2c24
RG
157 sqlite3_stmt* d_stmt{nullptr};
158 SSQLite3* d_db{nullptr};
159 int d_rc{0};
160 bool d_dolog;
161 bool d_prepared{false};
162
488f9e9e
AT
163 void prepareStatement() {
164 const char *pTail;
165
166 if (d_prepared) return;
167#if SQLITE_VERSION_NUMBER >= 3003009
168 if (sqlite3_prepare_v2(d_db->db(), d_query.c_str(), -1, &d_stmt, &pTail ) != SQLITE_OK)
169#else
170 if (sqlite3_prepare(d_db->db(), d_query.c_str(), -1, &d_stmt, &pTail ) != SQLITE_OK)
171#endif
172 {
173 releaseStatement();
7919673a 174 throw SSqlException(string("Unable to compile SQLite statement : '")+d_query+"': "+SSQLite3ErrorString(d_db->db()));
488f9e9e
AT
175 }
176 if (pTail && strlen(pTail)>0)
e6a9dde5 177 g_log<<Logger::Warning<<"Sqlite3 command partially processed. Unprocessed part: "<<pTail<<endl;
488f9e9e
AT
178 d_prepared = true;
179 }
180
181 void releaseStatement() {
182 if (d_stmt)
183 sqlite3_finalize(d_stmt);
eace2c24 184 d_stmt = nullptr;
488f9e9e
AT
185 d_prepared = false;
186 }
0f310932
AT
187};
188
0b67a76d 189// Constructor.
367f9b40 190SSQLite3::SSQLite3( const std::string & database, const std::string & journalmode, bool creat )
0b67a76d 191{
b6068fce
PL
192 if (access( database.c_str(), F_OK ) == -1){
193 if (!creat)
4419dfc6 194 throw sPerrorException( "SQLite database '"+database+"' does not exist yet" );
b6068fce
PL
195 } else {
196 if (creat)
197 throw sPerrorException( "SQLite database '"+database+"' already exists" );
198 }
0b67a76d
BH
199
200 if ( sqlite3_open( database.c_str(), &m_pDB)!=SQLITE_OK )
201 throw sPerrorException( "Could not connect to the SQLite database '" + database + "'" );
a464c196 202 m_dolog = 0;
0f310932 203 m_in_transaction = false;
cb8934ae 204 sqlite3_busy_handler(m_pDB, busyHandler, 0);
367f9b40
PD
205
206 if(journalmode.length())
207 execute("PRAGMA journal_mode="+journalmode);
0b67a76d
BH
208}
209
e237ea04 210void SSQLite3::setLog(bool state)
22cf81b0 211{
a464c196 212 m_dolog=state;
e237ea04
PD
213}
214
0b67a76d 215// Destructor.
6edbdc95 216SSQLite3::~SSQLite3()
0b67a76d 217{
6edbdc95 218 int ret;
0ba2a32b
BH
219 for(int n = 0; n < 2 ; ++n) {
220 if((ret =sqlite3_close( m_pDB )) != SQLITE_OK) {
0f310932 221 if(n || ret != SQLITE_BUSY) { // if we have SQLITE_BUSY, and a working m_Pstmt, try finalize
0ba2a32b
BH
222 cerr<<"Unable to close down sqlite connection: "<<ret<<endl;
223 abort();
224 }
0ba2a32b
BH
225 }
226 else
227 break;
6edbdc95 228 }
0b67a76d
BH
229}
230
a59a9c23
AT
231std::unique_ptr<SSqlStatement> SSQLite3::prepare(const string& query, int nparams __attribute__((unused))) {
232 return std::unique_ptr<SSqlStatement>(new SSQLite3Statement(this, m_dolog, query));
0b67a76d
BH
233}
234
0f310932
AT
235void SSQLite3::execute(const string& query) {
236 char *errmsg;
ca6b759c 237 std::string errstr1;
a5c2ec49
OM
238 int rc = sqlite3_exec(m_pDB, query.c_str(), nullptr, nullptr, &errmsg);
239 if (rc != SQLITE_OK) {
ca6b759c 240 errstr1 = errmsg;
e54d78b9 241 sqlite3_free(errmsg);
ca6b759c
OM
242 }
243 if (rc == SQLITE_BUSY) {
0f310932 244 if (m_in_transaction) {
a5c2ec49 245 throw SSqlException("Failed to execute query: " + errstr1);
0f310932 246 } else {
ca6b759c
OM
247 rc = sqlite3_exec(m_pDB, query.c_str(), NULL, NULL, &errmsg);
248 std::string errstr2;
a5c2ec49 249 if (rc != SQLITE_OK) {
ca6b759c 250 errstr2 = errmsg;
a955a010 251 sqlite3_free(errmsg);
a5c2ec49 252 throw SSqlException("Failed to execute query: " + errstr2);
a955a010 253 }
0f310932 254 }
a5c2ec49 255 } else if (rc != SQLITE_OK) {
4482ac79 256 throw SSqlException("Failed to execute query: " + errstr1);
0f310932 257 }
0b67a76d
BH
258}
259
cb8934ae
BH
260int SSQLite3::busyHandler(void*, int)
261{
de8ce191 262 Utility::usleep(1000);
cb8934ae
BH
263 return 1;
264}
0b67a76d 265
0f310932
AT
266void SSQLite3::startTransaction() {
267 execute("begin");
268 m_in_transaction = true;
269}
0b67a76d 270
0f310932
AT
271void SSQLite3::rollback() {
272 execute("rollback");
273 m_in_transaction = false;
0b67a76d
BH
274}
275
0f310932
AT
276void SSQLite3::commit() {
277 execute("commit");
278 m_in_transaction = false;
279}
0b67a76d 280
0f310932
AT
281// Constructs a SSqlException object.
282SSqlException SSQLite3::sPerrorException( const std::string & reason )
0b67a76d 283{
0f310932 284 return SSqlException( reason );
0b67a76d 285}