]>
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 int pdns_sqlite3_clear_bindings(sqlite3_stmt
*pStmt
){
43 for(i
=1; rc
==SQLITE_OK
&& i
<=sqlite3_bind_parameter_count(pStmt
); i
++){
44 rc
= sqlite3_bind_null(pStmt
, i
);
49 static string
SSQLite3ErrorString(sqlite3
*db
)
51 return string(sqlite3_errmsg(db
)+string(" (")+std::to_string(sqlite3_extended_errcode(db
))+string(")"));
54 class SSQLite3Statement
: public SSqlStatement
57 SSQLite3Statement(SSQLite3
*db
, bool dolog
, const string
& query
) :
64 int name2idx(const string
& name
) {
65 string zName
= string(":")+name
;
67 return sqlite3_bind_parameter_index(d_stmt
, zName
.c_str());
68 // XXX: support @ and $?
71 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; }
72 SSqlStatement
* bind(const string
& name
, int value
) { int idx
= name2idx(name
); if (idx
>0) { sqlite3_bind_int(d_stmt
, idx
, value
); }; return this; }
73 SSqlStatement
* bind(const string
& name
, uint32_t value
) { int idx
= name2idx(name
); if (idx
>0) { sqlite3_bind_int64(d_stmt
, idx
, value
); }; return this; }
74 SSqlStatement
* bind(const string
& name
, 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 value
) { int idx
= name2idx(name
); if (idx
>0) { sqlite3_bind_int64(d_stmt
, idx
, value
); }; return this; }
76 SSqlStatement
* bind(const string
& name
, long 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 long value
) { int idx
= name2idx(name
); if (idx
>0) { sqlite3_bind_int64(d_stmt
, idx
, value
); }; return this; }
78 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; }
79 SSqlStatement
* bindNull(const string
& name
) { int idx
= name2idx(name
); if (idx
>0) { sqlite3_bind_null(d_stmt
, idx
); }; return this; }
81 SSqlStatement
* execute() {
84 g_log
<<Logger::Warning
<< "Query "<<((long)(void*)this)<<": " << d_query
<< endl
;
87 int attempts
= d_db
->inTransaction(); // try only once
88 while(attempts
< 2 && (d_rc
= sqlite3_step(d_stmt
)) == SQLITE_BUSY
) attempts
++;
90 if (d_rc
!= SQLITE_ROW
&& d_rc
!= SQLITE_DONE
) {
93 if (d_rc
== SQLITE_CANTOPEN
)
94 throw SSqlException(string("CANTOPEN error in sqlite3, often caused by unwritable sqlite3 db *directory*: ")+SSQLite3ErrorString(d_db
->db()));
95 throw SSqlException(string("Error while retrieving SQLite query results: ")+SSQLite3ErrorString(d_db
->db()));
98 g_log
<<Logger::Warning
<< "Query "<<((long)(void*)this)<<": "<<d_dtime
.udiffNoReset()<<" usec to execute"<<endl
;
102 if(d_dolog
&& d_rc
!= SQLITE_ROW
) {
103 g_log
<<Logger::Warning
<< "Query "<<((long)(void*)this)<<": "<<d_dtime
.udiffNoReset()<<" total usec to last row"<<endl
;
105 return d_rc
== SQLITE_ROW
;
108 SSqlStatement
* nextRow(row_t
& row
) {
110 int numCols
= sqlite3_column_count(d_stmt
);
111 row
.reserve(numCols
); // preallocate memory
112 // Another row received, process it.
113 for ( int i
=0; i
<numCols
; i
++)
115 if (sqlite3_column_type(d_stmt
,i
) == SQLITE_NULL
) {
116 row
.emplace_back("");
118 const char *pData
= (const char*) sqlite3_column_text(d_stmt
, i
);
119 row
.emplace_back(pData
, sqlite3_column_bytes(d_stmt
, i
));
122 d_rc
= sqlite3_step(d_stmt
);
126 SSqlStatement
* getResult(result_t
& result
) {
128 while(hasNextRow()) {
131 result
.push_back(std::move(row
));
136 SSqlStatement
* reset() {
137 sqlite3_reset(d_stmt
);
138 #if SQLITE_VERSION_NUMBER >= 3003009
139 sqlite3_clear_bindings(d_stmt
);
141 pdns_sqlite3_clear_bindings(d_stmt
);
146 ~SSQLite3Statement() {
147 // deallocate if necessary
151 const string
& getQuery() { return d_query
; };
155 sqlite3_stmt
* d_stmt
{nullptr};
156 SSQLite3
* d_db
{nullptr};
159 bool d_prepared
{false};
161 void prepareStatement() {
164 if (d_prepared
) return;
165 #if SQLITE_VERSION_NUMBER >= 3003009
166 if (sqlite3_prepare_v2(d_db
->db(), d_query
.c_str(), -1, &d_stmt
, &pTail
) != SQLITE_OK
)
168 if (sqlite3_prepare(d_db
->db(), d_query
.c_str(), -1, &d_stmt
, &pTail
) != SQLITE_OK
)
172 throw SSqlException(string("Unable to compile SQLite statement : '")+d_query
+"': "+SSQLite3ErrorString(d_db
->db()));
174 if (pTail
&& strlen(pTail
)>0)
175 g_log
<<Logger::Warning
<<"Sqlite3 command partially processed. Unprocessed part: "<<pTail
<<endl
;
179 void releaseStatement() {
181 sqlite3_finalize(d_stmt
);
188 SSQLite3::SSQLite3( const std::string
& database
, const std::string
& journalmode
, bool creat
)
190 if (access( database
.c_str(), F_OK
) == -1){
192 throw sPerrorException( "SQLite database '"+database
+"' does not exist yet" );
195 throw sPerrorException( "SQLite database '"+database
+"' already exists" );
198 if ( sqlite3_open( database
.c_str(), &m_pDB
)!=SQLITE_OK
)
199 throw sPerrorException( "Could not connect to the SQLite database '" + database
+ "'" );
201 m_in_transaction
= false;
202 sqlite3_busy_handler(m_pDB
, busyHandler
, 0);
204 if(journalmode
.length())
205 execute("PRAGMA journal_mode="+journalmode
);
208 void SSQLite3::setLog(bool state
)
214 SSQLite3::~SSQLite3()
217 for(int n
= 0; n
< 2 ; ++n
) {
218 if((ret
=sqlite3_close( m_pDB
)) != SQLITE_OK
) {
219 if(n
|| ret
!= SQLITE_BUSY
) { // if we have SQLITE_BUSY, and a working m_Pstmt, try finalize
220 cerr
<<"Unable to close down sqlite connection: "<<ret
<<endl
;
229 std::unique_ptr
<SSqlStatement
> SSQLite3::prepare(const string
& query
, int nparams
__attribute__((unused
))) {
230 return std::unique_ptr
<SSqlStatement
>(new SSQLite3Statement(this, m_dolog
, query
));
233 void SSQLite3::execute(const string
& query
) {
236 if (sqlite3_exec(m_pDB
, query
.c_str(), NULL
, NULL
, &errmsg
) == SQLITE_BUSY
) {
237 if (m_in_transaction
) {
238 std::string
errstr(errmsg
);
239 sqlite3_free(errmsg
);
240 throw("Failed to execute query: " + errstr
);
242 if ((rc
= sqlite3_exec(m_pDB
, query
.c_str(), NULL
, NULL
, &errmsg
) != SQLITE_OK
) && rc
!= SQLITE_DONE
&& rc
!= SQLITE_ROW
) {
243 std::string
errstr(errmsg
);
244 sqlite3_free(errmsg
);
245 throw("Failed to execute query: " + errstr
);
251 int SSQLite3::busyHandler(void*, int)
253 Utility::usleep(1000);
257 void SSQLite3::startTransaction() {
259 m_in_transaction
= true;
262 void SSQLite3::rollback() {
264 m_in_transaction
= false;
267 void SSQLite3::commit() {
269 m_in_transaction
= false;
272 // Constructs a SSqlException object.
273 SSqlException
SSQLite3::sPerrorException( const std::string
& reason
)
275 return SSqlException( reason
);