]>
Commit | Line | Data |
---|---|---|
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 |
41 | static 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 |
51 | static string SSQLite3ErrorString(sqlite3 *db) |
52 | { | |
53 | return string(sqlite3_errmsg(db)+string(" (")+std::to_string(sqlite3_extended_errcode(db))+string(")")); | |
54 | } | |
55 | ||
22cf81b0 | 56 | class SSQLite3Statement: public SSqlStatement |
0f310932 AT |
57 | { |
58 | public: | |
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; }; | |
154 | private: | |
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 | 190 | SSQLite3::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 | 210 | void SSQLite3::setLog(bool state) |
22cf81b0 | 211 | { |
a464c196 | 212 | m_dolog=state; |
e237ea04 PD |
213 | } |
214 | ||
0b67a76d | 215 | // Destructor. |
6edbdc95 | 216 | SSQLite3::~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 |
231 | std::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 |
235 | void 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 |
260 | int SSQLite3::busyHandler(void*, int) |
261 | { | |
de8ce191 | 262 | Utility::usleep(1000); |
cb8934ae BH |
263 | return 1; |
264 | } | |
0b67a76d | 265 | |
0f310932 AT |
266 | void SSQLite3::startTransaction() { |
267 | execute("begin"); | |
268 | m_in_transaction = true; | |
269 | } | |
0b67a76d | 270 | |
0f310932 AT |
271 | void SSQLite3::rollback() { |
272 | execute("rollback"); | |
273 | m_in_transaction = false; | |
0b67a76d BH |
274 | } |
275 | ||
0f310932 AT |
276 | void SSQLite3::commit() { |
277 | execute("commit"); | |
278 | m_in_transaction = false; | |
279 | } | |
0b67a76d | 280 | |
0f310932 AT |
281 | // Constructs a SSqlException object. |
282 | SSqlException SSQLite3::sPerrorException( const std::string & reason ) | |
0b67a76d | 283 | { |
0f310932 | 284 | return SSqlException( reason ); |
0b67a76d | 285 | } |