]>
git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/ssqlite3.cc
1 /* SQLite backend for PowerDNS
2 * Copyright (C) 2003, Michel Stol <michel@powerdns.com>
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.
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.
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.
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
27 #include "ssqlite3.hh"
30 #include "pdns/logger.hh"
36 ** Set all the parameters in the compiled SQL statement to NULL.
38 * copied from sqlite 3.3.6 // cmouse
40 #if SQLITE_VERSION_NUMBER < 3003009
41 static int pdns_sqlite3_clear_bindings(sqlite3_stmt
*pStmt
){
44 for(i
=1; rc
==SQLITE_OK
&& i
<=sqlite3_bind_parameter_count(pStmt
); i
++){
45 rc
= sqlite3_bind_null(pStmt
, i
);
51 static string
SSQLite3ErrorString(sqlite3
*db
)
53 return string(sqlite3_errmsg(db
)+string(" (")+std::to_string(sqlite3_extended_errcode(db
))+string(")"));
56 class SSQLite3Statement
: public SSqlStatement
59 SSQLite3Statement(SSQLite3
*db
, bool dolog
, const string
& query
) :
66 int name2idx(const string
& name
) {
67 string zName
= string(":")+name
;
69 return sqlite3_bind_parameter_index(d_stmt
, zName
.c_str());
70 // XXX: support @ and $?
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; }
83 SSqlStatement
* execute() {
86 g_log
<<Logger::Warning
<< "Query "<<((long)(void*)this)<<": " << d_query
<< endl
;
89 int attempts
= d_db
->inTransaction(); // try only once
90 while(attempts
< 2 && (d_rc
= sqlite3_step(d_stmt
)) == SQLITE_BUSY
) attempts
++;
92 if (d_rc
!= SQLITE_ROW
&& d_rc
!= SQLITE_DONE
) {
95 if (d_rc
== SQLITE_CANTOPEN
)
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()));
100 g_log
<<Logger::Warning
<< "Query "<<((long)(void*)this)<<": "<<d_dtime
.udiffNoReset()<<" usec to execute"<<endl
;
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
;
107 return d_rc
== SQLITE_ROW
;
110 SSqlStatement
* nextRow(row_t
& row
) {
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
++)
117 if (sqlite3_column_type(d_stmt
,i
) == SQLITE_NULL
) {
118 row
.emplace_back("");
120 const char *pData
= (const char*) sqlite3_column_text(d_stmt
, i
);
121 row
.emplace_back(pData
, sqlite3_column_bytes(d_stmt
, i
));
124 d_rc
= sqlite3_step(d_stmt
);
128 SSqlStatement
* getResult(result_t
& result
) {
130 while(hasNextRow()) {
133 result
.push_back(std::move(row
));
138 SSqlStatement
* reset() {
139 sqlite3_reset(d_stmt
);
140 #if SQLITE_VERSION_NUMBER >= 3003009
141 sqlite3_clear_bindings(d_stmt
);
143 pdns_sqlite3_clear_bindings(d_stmt
);
148 ~SSQLite3Statement() {
149 // deallocate if necessary
153 const string
& getQuery() { return d_query
; };
157 sqlite3_stmt
* d_stmt
{nullptr};
158 SSQLite3
* d_db
{nullptr};
161 bool d_prepared
{false};
163 void prepareStatement() {
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
)
170 if (sqlite3_prepare(d_db
->db(), d_query
.c_str(), -1, &d_stmt
, &pTail
) != SQLITE_OK
)
174 throw SSqlException(string("Unable to compile SQLite statement : '")+d_query
+"': "+SSQLite3ErrorString(d_db
->db()));
176 if (pTail
&& strlen(pTail
)>0)
177 g_log
<<Logger::Warning
<<"Sqlite3 command partially processed. Unprocessed part: "<<pTail
<<endl
;
181 void releaseStatement() {
183 sqlite3_finalize(d_stmt
);
190 SSQLite3::SSQLite3( const std::string
& database
, const std::string
& journalmode
, bool creat
)
192 if (access( database
.c_str(), F_OK
) == -1){
194 throw sPerrorException( "SQLite database '"+database
+"' does not exist yet" );
197 throw sPerrorException( "SQLite database '"+database
+"' already exists" );
200 if ( sqlite3_open( database
.c_str(), &m_pDB
)!=SQLITE_OK
)
201 throw sPerrorException( "Could not connect to the SQLite database '" + database
+ "'" );
203 m_in_transaction
= false;
204 sqlite3_busy_handler(m_pDB
, busyHandler
, 0);
206 if(journalmode
.length())
207 execute("PRAGMA journal_mode="+journalmode
);
210 void SSQLite3::setLog(bool state
)
216 SSQLite3::~SSQLite3()
219 for(int n
= 0; n
< 2 ; ++n
) {
220 if((ret
=sqlite3_close( m_pDB
)) != SQLITE_OK
) {
221 if(n
|| ret
!= SQLITE_BUSY
) { // if we have SQLITE_BUSY, and a working m_Pstmt, try finalize
222 cerr
<<"Unable to close down sqlite connection: "<<ret
<<endl
;
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
));
235 void SSQLite3::execute(const string
& query
) {
238 int rc
= sqlite3_exec(m_pDB
, query
.c_str(), nullptr, nullptr, &errmsg
);
239 if (rc
!= SQLITE_OK
) {
241 sqlite3_free(errmsg
);
243 if (rc
== SQLITE_BUSY
) {
244 if (m_in_transaction
) {
245 throw SSqlException("Failed to execute query: " + errstr1
);
247 rc
= sqlite3_exec(m_pDB
, query
.c_str(), NULL
, NULL
, &errmsg
);
249 if (rc
!= SQLITE_OK
) {
251 sqlite3_free(errmsg
);
252 throw SSqlException("Failed to execute query: " + errstr2
);
255 } else if (rc
!= SQLITE_OK
) {
256 throw SSqlException("Failed to execute query: " + errstr1
);
260 int SSQLite3::busyHandler(void*, int)
262 Utility::usleep(1000);
266 void SSQLite3::startTransaction() {
268 m_in_transaction
= true;
271 void SSQLite3::rollback() {
273 m_in_transaction
= false;
276 void SSQLite3::commit() {
278 m_in_transaction
= false;
281 // Constructs a SSqlException object.
282 SSqlException
SSQLite3::sPerrorException( const std::string
& reason
)
284 return SSqlException( reason
);