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