]>
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 AT |
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 | ||
22cf81b0 | 49 | class SSQLite3Statement: public SSqlStatement |
0f310932 AT |
50 | { |
51 | public: | |
488f9e9e | 52 | SSQLite3Statement(SSQLite3 *db, bool dolog, const string& query) : d_prepared(false) |
0f310932 | 53 | { |
0f310932 AT |
54 | this->d_query = query; |
55 | this->d_dolog = dolog; | |
488f9e9e | 56 | d_stmt = NULL; |
acd55b9a | 57 | d_rc = 0; |
0f310932 | 58 | d_db = db; |
0f310932 AT |
59 | } |
60 | ||
61 | int name2idx(const string& name) { | |
62 | string zName = string(":")+name; | |
488f9e9e | 63 | prepareStatement(); |
0f310932 | 64 | return sqlite3_bind_parameter_index(d_stmt, zName.c_str()); |
22cf81b0 | 65 | // XXX: support @ and $? |
0f310932 AT |
66 | } |
67 | ||
68 | 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; } | |
69 | SSqlStatement* bind(const string& name, int value) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_int(d_stmt, idx, value); }; return this; } | |
70 | SSqlStatement* bind(const string& name, uint32_t value) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_int64(d_stmt, idx, value); }; return this; } | |
71 | SSqlStatement* bind(const string& name, long value) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_int64(d_stmt, idx, value); }; return this; } | |
72 | SSqlStatement* bind(const string& name, unsigned long value) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_int64(d_stmt, idx, value); }; return this; } | |
73 | SSqlStatement* bind(const string& name, long long value) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_int64(d_stmt, idx, value); }; return this; }; | |
74 | 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; } | |
75 | 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; } | |
76 | SSqlStatement* bindNull(const string& name) { int idx = name2idx(name); if (idx>0) { sqlite3_bind_null(d_stmt, idx); }; return this; } | |
77 | ||
78 | SSqlStatement* execute() { | |
488f9e9e | 79 | prepareStatement(); |
a00998f7 | 80 | if (d_dolog) |
e6a9dde5 | 81 | g_log<<Logger::Warning<< "Query: " << d_query << endl; |
0f310932 AT |
82 | int attempts = d_db->inTransaction(); // try only once |
83 | while(attempts < 2 && (d_rc = sqlite3_step(d_stmt)) == SQLITE_BUSY) attempts++; | |
84 | ||
85 | if (d_rc != SQLITE_ROW && d_rc != SQLITE_DONE) { | |
86 | // failed. | |
488f9e9e AT |
87 | releaseStatement(); |
88 | if (d_rc == SQLITE_CANTOPEN) | |
0f310932 AT |
89 | throw SSqlException(string("CANTOPEN error in sqlite3, often caused by unwritable sqlite3 db *directory*: ")+string(sqlite3_errmsg(d_db->db()))); |
90 | throw SSqlException(string("Error while retrieving SQLite query results: ")+string(sqlite3_errmsg(d_db->db()))); | |
91 | } | |
92 | return this; | |
93 | } | |
94 | bool hasNextRow() { return d_rc == SQLITE_ROW; } | |
95 | ||
96 | SSqlStatement* nextRow(row_t& row) { | |
97 | row.clear(); | |
98 | int numCols = sqlite3_column_count(d_stmt); | |
99 | row.reserve(numCols); // preallocate memory | |
100 | // Another row received, process it. | |
101 | for ( int i=0; i<numCols; i++) | |
102 | { | |
103 | if (sqlite3_column_type(d_stmt,i) == SQLITE_NULL) { | |
104 | row.push_back(""); | |
105 | } else { | |
106 | const char *pData = (const char*) sqlite3_column_text(d_stmt, i); | |
22cf81b0 | 107 | row.push_back(string(pData, sqlite3_column_bytes(d_stmt, i))); |
0f310932 AT |
108 | } |
109 | } | |
110 | d_rc = sqlite3_step(d_stmt); | |
111 | return this; | |
112 | } | |
113 | ||
114 | SSqlStatement* getResult(result_t& result) { | |
115 | result.clear(); | |
116 | while(hasNextRow()) { | |
117 | row_t row; | |
118 | nextRow(row); | |
119 | result.push_back(row); | |
120 | } | |
121 | return this; | |
122 | } | |
123 | ||
124 | SSqlStatement* reset() { | |
125 | sqlite3_reset(d_stmt); | |
126 | #if SQLITE_VERSION_NUMBER >= 3003009 | |
127 | sqlite3_clear_bindings(d_stmt); | |
128 | #else | |
129 | pdns_sqlite3_clear_bindings(d_stmt); | |
130 | #endif | |
131 | return this; | |
132 | } | |
133 | ||
134 | ~SSQLite3Statement() { | |
135 | // deallocate if necessary | |
488f9e9e | 136 | releaseStatement(); |
0f310932 AT |
137 | } |
138 | ||
139 | const string& getQuery() { return d_query; }; | |
140 | private: | |
141 | string d_query; | |
142 | sqlite3_stmt* d_stmt; | |
0f310932 | 143 | SSQLite3* d_db; |
1fd46cb0 | 144 | int d_rc; |
0f310932 | 145 | bool d_dolog; |
488f9e9e AT |
146 | bool d_prepared; |
147 | ||
148 | void prepareStatement() { | |
149 | const char *pTail; | |
150 | ||
151 | if (d_prepared) return; | |
152 | #if SQLITE_VERSION_NUMBER >= 3003009 | |
153 | if (sqlite3_prepare_v2(d_db->db(), d_query.c_str(), -1, &d_stmt, &pTail ) != SQLITE_OK) | |
154 | #else | |
155 | if (sqlite3_prepare(d_db->db(), d_query.c_str(), -1, &d_stmt, &pTail ) != SQLITE_OK) | |
156 | #endif | |
157 | { | |
158 | releaseStatement(); | |
159 | throw SSqlException(string("Unable to compile SQLite statement : '")+d_query+"': "+sqlite3_errmsg(d_db->db())); | |
160 | } | |
161 | if (pTail && strlen(pTail)>0) | |
e6a9dde5 | 162 | g_log<<Logger::Warning<<"Sqlite3 command partially processed. Unprocessed part: "<<pTail<<endl; |
488f9e9e AT |
163 | d_prepared = true; |
164 | } | |
165 | ||
166 | void releaseStatement() { | |
167 | if (d_stmt) | |
168 | sqlite3_finalize(d_stmt); | |
169 | d_stmt = NULL; | |
170 | d_prepared = false; | |
171 | } | |
0f310932 AT |
172 | }; |
173 | ||
0b67a76d | 174 | // Constructor. |
ccca2f64 | 175 | SSQLite3::SSQLite3( const std::string & database, bool creat ) |
0b67a76d | 176 | { |
b6068fce PL |
177 | if (access( database.c_str(), F_OK ) == -1){ |
178 | if (!creat) | |
4419dfc6 | 179 | throw sPerrorException( "SQLite database '"+database+"' does not exist yet" ); |
b6068fce PL |
180 | } else { |
181 | if (creat) | |
182 | throw sPerrorException( "SQLite database '"+database+"' already exists" ); | |
183 | } | |
0b67a76d BH |
184 | |
185 | if ( sqlite3_open( database.c_str(), &m_pDB)!=SQLITE_OK ) | |
186 | throw sPerrorException( "Could not connect to the SQLite database '" + database + "'" ); | |
a464c196 | 187 | m_dolog = 0; |
0f310932 | 188 | m_in_transaction = false; |
cb8934ae | 189 | sqlite3_busy_handler(m_pDB, busyHandler, 0); |
0b67a76d BH |
190 | } |
191 | ||
e237ea04 | 192 | void SSQLite3::setLog(bool state) |
22cf81b0 | 193 | { |
a464c196 | 194 | m_dolog=state; |
e237ea04 PD |
195 | } |
196 | ||
0b67a76d | 197 | // Destructor. |
6edbdc95 | 198 | SSQLite3::~SSQLite3() |
0b67a76d | 199 | { |
6edbdc95 | 200 | int ret; |
0ba2a32b BH |
201 | for(int n = 0; n < 2 ; ++n) { |
202 | if((ret =sqlite3_close( m_pDB )) != SQLITE_OK) { | |
0f310932 | 203 | if(n || ret != SQLITE_BUSY) { // if we have SQLITE_BUSY, and a working m_Pstmt, try finalize |
0ba2a32b BH |
204 | cerr<<"Unable to close down sqlite connection: "<<ret<<endl; |
205 | abort(); | |
206 | } | |
0ba2a32b BH |
207 | } |
208 | else | |
209 | break; | |
6edbdc95 | 210 | } |
0b67a76d BH |
211 | } |
212 | ||
a59a9c23 AT |
213 | std::unique_ptr<SSqlStatement> SSQLite3::prepare(const string& query, int nparams __attribute__((unused))) { |
214 | return std::unique_ptr<SSqlStatement>(new SSQLite3Statement(this, m_dolog, query)); | |
0b67a76d BH |
215 | } |
216 | ||
0f310932 AT |
217 | void SSQLite3::execute(const string& query) { |
218 | char *errmsg; | |
219 | int rc; | |
220 | if (sqlite3_exec(m_pDB, query.c_str(), NULL, NULL, &errmsg) == SQLITE_BUSY) { | |
221 | if (m_in_transaction) { | |
222 | throw("Failed to execute query: " + string(errmsg)); | |
223 | } else { | |
224 | if ((rc = sqlite3_exec(m_pDB, query.c_str(), NULL, NULL, &errmsg) != SQLITE_OK) && rc != SQLITE_DONE && rc != SQLITE_ROW) | |
225 | throw("Failed to execute query: " + string(errmsg)); | |
226 | } | |
227 | } | |
0b67a76d BH |
228 | } |
229 | ||
cb8934ae BH |
230 | int SSQLite3::busyHandler(void*, int) |
231 | { | |
de8ce191 | 232 | Utility::usleep(1000); |
cb8934ae BH |
233 | return 1; |
234 | } | |
0b67a76d | 235 | |
0f310932 AT |
236 | void SSQLite3::startTransaction() { |
237 | execute("begin"); | |
238 | m_in_transaction = true; | |
239 | } | |
0b67a76d | 240 | |
0f310932 AT |
241 | void SSQLite3::rollback() { |
242 | execute("rollback"); | |
243 | m_in_transaction = false; | |
0b67a76d BH |
244 | } |
245 | ||
0f310932 AT |
246 | void SSQLite3::commit() { |
247 | execute("commit"); | |
248 | m_in_transaction = false; | |
249 | } | |
0b67a76d | 250 | |
0f310932 AT |
251 | // Constructs a SSqlException object. |
252 | SSqlException SSQLite3::sPerrorException( const std::string & reason ) | |
0b67a76d | 253 | { |
0f310932 | 254 | return SSqlException( reason ); |
0b67a76d | 255 | } |