]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/ssqlite3.cc
Merge pull request #5461 from rgacogne/rec-cache-index
[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
AT
39*/
40int 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 49class SSQLite3Statement: public SSqlStatement
0f310932
AT
50{
51public:
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
AT
80 if (d_dolog)
81 L<<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; };
140private:
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)
162 L<<Logger::Warning<<"Sqlite3 command partially processed. Unprocessed part: "<<pTail<<endl;
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 175SSQLite3::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 192void SSQLite3::setLog(bool state)
22cf81b0 193{
a464c196 194 m_dolog=state;
e237ea04
PD
195}
196
0b67a76d 197// Destructor.
6edbdc95 198SSQLite3::~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
0f310932
AT
213SSqlStatement* SSQLite3::prepare(const string& query, int nparams __attribute__((unused))) {
214 return new SSQLite3Statement(this, m_dolog, query);
0b67a76d
BH
215}
216
0f310932
AT
217void 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
230int SSQLite3::busyHandler(void*, int)
231{
de8ce191 232 Utility::usleep(1000);
cb8934ae
BH
233 return 1;
234}
0b67a76d 235
0f310932
AT
236void SSQLite3::startTransaction() {
237 execute("begin");
238 m_in_transaction = true;
239}
0b67a76d 240
0f310932
AT
241void SSQLite3::rollback() {
242 execute("rollback");
243 m_in_transaction = false;
0b67a76d
BH
244}
245
0f310932
AT
246void SSQLite3::commit() {
247 execute("commit");
248 m_in_transaction = false;
249}
0b67a76d 250
0f310932
AT
251// Constructs a SSqlException object.
252SSqlException SSQLite3::sPerrorException( const std::string & reason )
0b67a76d 253{
0f310932 254 return SSqlException( reason );
0b67a76d 255}