]> git.ipfire.org Git - thirdparty/pdns.git/blame - modules/godbcbackend/sodbc.cc
Merge pull request #11431 from jroessler-ox/docs-kskzskroll-update
[thirdparty/pdns.git] / modules / godbcbackend / sodbc.cc
CommitLineData
12471842
PL
1/*
2 * This file is part of PowerDNS or dnsdist.
3 * Copyright -- PowerDNS.COM B.V. and its contributors
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of version 2 of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * In addition, for the avoidance of any doubt, permission is granted to
10 * link this program with OpenSSL and to (re)distribute the binaries
11 * produced as the result of such linking.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
d2bc6b27 22#include "pdns/logger.hh"
4f983d1b
PD
23#include "pdns/utility.hh"
24#include <sstream>
25#include "sodbc.hh"
26#include <string.h>
27
ff05c7e1 28static bool realTestResult(SQLRETURN result, SQLSMALLINT type, SQLHANDLE handle, const std::string& message, std::string& errorMessage)
4f983d1b
PD
29{
30 // cerr<<"result = "<<result<<endl;
ff05c7e1 31 if (result == SQL_SUCCESS || result == SQL_SUCCESS_WITH_INFO)
21143435 32 return true;
4f983d1b
PD
33
34 ostringstream errmsg;
35
36 errmsg << message << ": ";
37
ff05c7e1
O
38 if (result != SQL_ERROR && result != SQL_SUCCESS_WITH_INFO) {
39 cerr << "handle " << handle << " got result " << result << endl;
40 errmsg << "SQL function returned " << result << ", no additional information available" << endl;
21143435
AT
41 errorMessage = errmsg.str();
42 return false;
4f983d1b
PD
43 }
44
45 SQLINTEGER i = 0;
46 SQLINTEGER native;
ff05c7e1 47 SQLCHAR state[7];
4f983d1b
PD
48 SQLCHAR text[256];
49 SQLSMALLINT len;
50 SQLRETURN ret;
51
ff05c7e1 52 do {
4f983d1b
PD
53 // cerr<<"getting sql diag record "<<i<<endl;
54 ret = SQLGetDiagRec(type, handle, ++i, state, &native, text,
7ca490f0 55 sizeof(text), &len);
4f983d1b
PD
56 // cerr<<"getdiagrec said "<<ret<<endl;
57 if (SQL_SUCCEEDED(ret)) { // cerr<<"got it"<<endl;
ff05c7e1 58 errmsg << state << i << native << text << "/";
4f983d1b 59 }
ff05c7e1 60 } while (ret == SQL_SUCCESS);
21143435
AT
61 errorMessage = errmsg.str();
62 return false;
4f983d1b
PD
63}
64
ff05c7e1 65class SODBCStatement : public SSqlStatement
4f983d1b
PD
66{
67public:
68 SODBCStatement(const string& query, bool dolog, int nparams, SQLHDBC connection)
69 {
4f983d1b 70 d_query = query;
4f983d1b
PD
71 d_conn = connection;
72 d_dolog = dolog;
73 d_residx = 0;
74 d_paridx = 0;
34c513f9 75 d_result = SQL_NO_DATA;
21143435
AT
76 d_statement = NULL;
77 d_prepared = false;
34c513f9 78 m_columncount = 0;
4f983d1b 79 d_parnum = nparams;
4f983d1b
PD
80 }
81
ff05c7e1
O
82 struct ODBCParam
83 {
84 SQLPOINTER ParameterValuePtr;
85 SQLLEN* LenPtr;
86 SQLSMALLINT ParameterType;
87 SQLSMALLINT ValueType;
fb14a220 88 };
4f983d1b
PD
89
90 vector<ODBCParam> d_req_bind;
91
d73de874 92 SSqlStatement* bind(const string& /* name */, ODBCParam& p)
ff05c7e1 93 {
1bc2ed37 94 prepareStatement();
4f983d1b 95 d_req_bind.push_back(p);
ecc07c9d 96 SQLLEN ColumnSize = (p.ParameterType == SQL_VARCHAR) ? *(p.LenPtr) : 0;
4f983d1b 97 SQLRETURN result = SQLBindParameter(
ff05c7e1
O
98 d_statement, // StatementHandle,
99 d_paridx + 1, // ParameterNumber,
100 SQL_PARAM_INPUT, // InputOutputType,
101 p.ValueType, // ValueType,
102 p.ParameterType, // ParameterType,
ecc07c9d 103 ColumnSize, // ColumnSize,
ff05c7e1
O
104 0, // DecimalDigits,
105 p.ParameterValuePtr, // ParameterValuePtr,
106 0, // BufferLength,
107 p.LenPtr // StrLen_or_IndPtr
4f983d1b 108 );
ff05c7e1 109 testResult(result, SQL_HANDLE_STMT, d_statement, "Could not bind parameter.");
4f983d1b
PD
110 d_paridx++;
111
112 return this;
113 }
fb14a220 114
9a315393 115 SSqlStatement* bind(const string& name, bool value) override
ff05c7e1
O
116 {
117 prepareStatement();
118 return bind(name, (uint32_t)value);
119 }
fb14a220 120
9a315393 121 SSqlStatement* bind(const string& name, long value) override
ff05c7e1
O
122 {
123 prepareStatement();
124 return bind(name, (unsigned long)value);
125 }
fb14a220 126
9a315393 127 SSqlStatement* bind(const string& name, int value) override
ff05c7e1
O
128 {
129 prepareStatement();
130 return bind(name, (uint32_t)value);
131 }
fb14a220 132
9a315393 133 SSqlStatement* bind(const string& name, long long value) override
ff05c7e1
O
134 {
135 prepareStatement();
136 return bind(name, (unsigned long long)value);
137 }
fb14a220 138
9a315393 139 SSqlStatement* bind(const string& name, uint32_t value) override
ff05c7e1 140 {
1bc2ed37 141 prepareStatement();
fb14a220 142 ODBCParam p;
ff05c7e1
O
143 p.ParameterValuePtr = new UDWORD{value};
144 p.LenPtr = new SQLLEN{sizeof(UDWORD)};
fb14a220
AT
145 p.ParameterType = SQL_INTEGER;
146 p.ValueType = SQL_INTEGER;
147 return bind(name, p);
148 }
149
9a315393 150 SSqlStatement* bind(const string& name, unsigned long value) override
ff05c7e1 151 {
1bc2ed37 152 prepareStatement();
fb14a220 153 ODBCParam p;
ff05c7e1
O
154 p.ParameterValuePtr = new ULONG{value};
155 p.LenPtr = new SQLLEN{sizeof(ULONG)};
fb14a220
AT
156 p.ParameterType = SQL_INTEGER;
157 p.ValueType = SQL_INTEGER;
158 return bind(name, p);
159 }
160
9a315393 161 SSqlStatement* bind(const string& name, unsigned long long value) override
ff05c7e1 162 {
1bc2ed37 163 prepareStatement();
fb14a220 164 ODBCParam p;
ff05c7e1
O
165 p.ParameterValuePtr = new unsigned long long{value};
166 p.LenPtr = new SQLLEN{sizeof(unsigned long long)};
fb14a220
AT
167 p.ParameterType = SQL_BIGINT;
168 p.ValueType = SQL_C_UBIGINT;
169 return bind(name, p);
170 }
171
9a315393 172 SSqlStatement* bind(const string& name, const std::string& value) override
ff05c7e1 173 {
4f983d1b
PD
174
175 // cerr<<"asked to bind string "<<value<<endl;
176
ff05c7e1
O
177 if (d_req_bind.size() > (d_parnum + 1))
178 throw SSqlException("Trying to bind too many parameters.");
1bc2ed37 179 prepareStatement();
4f983d1b
PD
180 ODBCParam p;
181
ff05c7e1 182 p.ParameterValuePtr = (char*)new char[value.size() + 1];
4f983d1b 183 value.copy((char*)p.ParameterValuePtr, value.size());
ff05c7e1
O
184 ((char*)p.ParameterValuePtr)[value.size()] = 0;
185 p.LenPtr = new SQLLEN;
186 *(p.LenPtr) = value.size();
fb14a220
AT
187 p.ParameterType = SQL_VARCHAR;
188 p.ValueType = SQL_C_CHAR;
4f983d1b 189
fb14a220 190 return bind(name, p);
4f983d1b
PD
191 }
192
9a315393 193 SSqlStatement* bindNull(const string& name) override
ff05c7e1
O
194 {
195 if (d_req_bind.size() > (d_parnum + 1))
196 throw SSqlException("Trying to bind too many parameters.");
4f983d1b 197
1bc2ed37 198 prepareStatement();
4f983d1b
PD
199 ODBCParam p;
200
201 p.ParameterValuePtr = NULL;
ff05c7e1
O
202 p.LenPtr = new SQLLEN;
203 *(p.LenPtr) = SQL_NULL_DATA;
fb14a220
AT
204 p.ParameterType = SQL_VARCHAR;
205 p.ValueType = SQL_C_CHAR;
4f983d1b 206
fb14a220 207 return bind(name, p);
4f983d1b
PD
208 }
209
9a315393 210 SSqlStatement* execute() override
4f983d1b 211 {
1bc2ed37 212 prepareStatement();
4f983d1b
PD
213 SQLRETURN result;
214 // cerr<<"execute("<<d_query<<")"<<endl;
215 if (d_dolog) {
ff05c7e1 216 g_log << Logger::Warning << "Query: " << d_query << endl;
4f983d1b
PD
217 }
218
219 result = SQLExecute(d_statement);
ff05c7e1
O
220 if (result != SQL_NO_DATA) // odbc+sqlite returns this on 'no rows updated'
221 testResult(result, SQL_HANDLE_STMT, d_statement, "Could not execute query (" + d_query + ").");
4f983d1b
PD
222
223 // Determine the number of columns.
ff05c7e1
O
224 result = SQLNumResultCols(d_statement, &m_columncount);
225 testResult(result, SQL_HANDLE_STMT, d_statement, "Could not determine the number of columns.");
4f983d1b
PD
226 // cerr<<"got "<<m_columncount<<" columns"<<endl;
227
ff05c7e1 228 if (m_columncount) {
4f983d1b
PD
229 // cerr<<"first SQLFetch"<<endl;
230 d_result = SQLFetch(d_statement);
231 // cerr<<"first SQLFetch done, d_result="<<d_result<<endl;
232 }
233 else
234 d_result = SQL_NO_DATA;
235
ff05c7e1
O
236 if (d_result != SQL_NO_DATA)
237 testResult(d_result, SQL_HANDLE_STMT, d_statement, "Could not do first SQLFetch for (" + d_query + ").");
4f983d1b
PD
238 return this;
239 }
240
9a315393 241 bool hasNextRow() override
ff05c7e1 242 {
4f983d1b 243 // cerr<<"hasNextRow d_result="<<d_result<<endl;
ff05c7e1 244 return d_result != SQL_NO_DATA;
4f983d1b 245 }
9a315393 246 SSqlStatement* nextRow(row_t& row) override;
4f983d1b 247
9a315393 248 SSqlStatement* getResult(result_t& result) override
ff05c7e1 249 {
4f983d1b
PD
250 result.clear();
251 // if (d_res == NULL) return this;
252 row_t row;
ff05c7e1
O
253 while (hasNextRow()) {
254 nextRow(row);
255 result.push_back(row);
256 }
4f983d1b
PD
257 return this;
258 }
259
9a315393 260 SSqlStatement* reset() override
ff05c7e1 261 {
4f983d1b
PD
262 SQLCloseCursor(d_statement); // hack, this probably violates some state transitions
263
ff05c7e1
O
264 for (auto& i : d_req_bind) {
265 if (i.ParameterType == SQL_VARCHAR)
266 delete[](char*) i.ParameterValuePtr;
267 else if (i.ParameterType == SQL_INTEGER)
268 delete (ULONG*)i.ParameterValuePtr;
269 else if (i.ParameterType == SQL_C_UBIGINT)
270 delete (unsigned long long*)i.ParameterValuePtr;
fb14a220
AT
271 delete i.LenPtr;
272 }
4f983d1b
PD
273 d_req_bind.clear();
274 d_residx = 0;
275 d_paridx = 0;
276 return this;
277 }
9a315393 278 const std::string& getQuery() override { return d_query; }
4f983d1b 279
9a315393 280 ~SODBCStatement() override
ff05c7e1 281 {
21143435
AT
282 releaseStatement();
283 }
21143435 284
ff05c7e1
O
285private:
286 void testResult(SQLRETURN result, SQLSMALLINT type, SQLHANDLE handle, const std::string& message)
287 {
288 std::string errorMessage;
289 if (!realTestResult(result, type, handle, message, errorMessage)) {
290 releaseStatement();
291 throw SSqlException(errorMessage);
292 }
21143435
AT
293 }
294
ff05c7e1
O
295 void releaseStatement()
296 {
21143435
AT
297 reset();
298 if (d_statement != NULL)
299 SQLFreeHandle(SQL_HANDLE_STMT, d_statement);
300 d_prepared = false;
301 }
302
ff05c7e1
O
303 void prepareStatement()
304 {
305 if (d_prepared)
306 return;
21143435
AT
307
308 SQLRETURN result;
309
310 // Allocate statement handle.
ff05c7e1
O
311 result = SQLAllocHandle(SQL_HANDLE_STMT, d_conn, &d_statement);
312 testResult(result, SQL_HANDLE_DBC, d_conn, "Could not allocate a statement handle.");
21143435 313
ff05c7e1
O
314 result = SQLPrepare(d_statement, (SQLCHAR*)d_query.c_str(), SQL_NTS);
315 testResult(result, SQL_HANDLE_STMT, d_statement, "Could not prepare query.");
21143435
AT
316
317 SQLSMALLINT paramcount;
318 result = SQLNumParams(d_statement, &paramcount);
ff05c7e1 319 testResult(result, SQL_HANDLE_STMT, d_statement, "Could not get parameter count.");
21143435
AT
320
321 if (paramcount != static_cast<SQLSMALLINT>(d_parnum)) {
322 releaseStatement();
323 throw SSqlException("Provided parameter count does not match statement: " + d_query);
324 }
325
326 // cerr<<"prepared ("<<query<<")"<<endl;
327 d_prepared = true;
328 }
329
4f983d1b
PD
330 string d_query;
331 bool d_dolog;
21143435 332 bool d_prepared;
3a204960 333 int d_residx;
ff05c7e1 334 size_t d_paridx, d_parnum;
4f983d1b
PD
335 SQLRETURN d_result;
336
337 SQLHDBC d_conn;
ff05c7e1 338 SQLHSTMT d_statement; //!< Database statement handle.
4f983d1b
PD
339
340 //! Column type.
341 struct column_t
342 {
ff05c7e1
O
343 SQLSMALLINT m_type; //!< Type of the column.
344 SQLULEN m_size; //!< Column size.
345 SQLPOINTER m_pData; //!< Pointer to the memory where to store the data.
346 bool m_canBeNull; //!< Can this column be null?
4f983d1b
PD
347 };
348
349 //! Column info.
350 SQLSMALLINT m_columncount;
4f983d1b
PD
351};
352
353SSqlStatement* SODBCStatement::nextRow(row_t& row)
354{
355 SQLRETURN result;
356
357 row.clear();
358
359 result = d_result;
360 // cerr<<"at start of nextRow, previous SQLFetch result is "<<result<<endl;
361 // FIXME handle errors (SQL_NO_DATA==100, anything other than the two SUCCESS options below is bad news)
ff05c7e1 362 if (result == SQL_SUCCESS || result == SQL_SUCCESS_WITH_INFO) {
4f983d1b
PD
363 // cerr<<"got row"<<endl;
364 // We've got a data row, now lets get the results.
ff05c7e1 365 for (int i = 0; i < m_columncount; i++) {
fdeaea1f 366 SQLLEN len;
ff05c7e1 367 SQLCHAR coldata[128 * 1024];
5f832d98 368 std::string strres = "";
ff05c7e1
O
369 result = SQLGetData(d_statement, i + 1, SQL_C_CHAR, (SQLPOINTER)coldata, sizeof(coldata), &len);
370 testResult(result, SQL_HANDLE_STMT, d_statement, "Could not get data.");
5f832d98 371 if (len > SQL_NULL_DATA)
ff05c7e1 372 strres = std::string(reinterpret_cast<const char*>(coldata), std::min<SQLLEN>(sizeof(coldata) - 1, len)); // do not use nil byte
5f832d98 373 row.push_back(strres);
4f983d1b
PD
374 }
375
376 // Done!
377 d_residx++;
378 // cerr<<"SQLFetch"<<endl;
379 d_result = SQLFetch(d_statement);
380 // cerr<<"subsequent SQLFetch done, d_result="<<d_result<<endl;
ff05c7e1 381 if (d_result == SQL_NO_DATA) {
e35f9e46
OM
382 SQLRETURN result2 = SQLMoreResults(d_statement);
383 // cerr<<"SQLMoreResults done, result="<<d_result2<<endl;
384 if (result2 == SQL_NO_DATA) {
385 d_result = result2;
4f983d1b
PD
386 }
387 else {
ff05c7e1
O
388 testResult(result2, SQL_HANDLE_STMT, d_statement, "Could not fetch next result set for (" + d_query + ").");
389 d_result = SQLFetch(d_statement);
4f983d1b
PD
390 }
391 }
ff05c7e1 392 testResult(result, SQL_HANDLE_STMT, d_statement, "Could not do subsequent SQLFetch for (" + d_query + ").");
4f983d1b
PD
393
394 return this;
395 }
396
ff05c7e1
O
397 SQLFreeStmt(d_statement, SQL_CLOSE);
398 throw SSqlException("Should not get here.");
4f983d1b
PD
399}
400
401// Constructor.
402SODBC::SODBC(
ff05c7e1
O
403 const std::string& dsn,
404 const std::string& username,
405 const std::string& password)
4f983d1b 406{
ff05c7e1 407 SQLRETURN result;
4f983d1b
PD
408
409 // Allocate an environment handle.
ff05c7e1
O
410 result = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &m_environment);
411 testResult(result, SQL_NULL_HANDLE, NULL, "Could not allocate an environment handle.");
4f983d1b
PD
412
413 // Set ODBC version. (IEUW!)
ff05c7e1
O
414 result = SQLSetEnvAttr(m_environment, SQL_ATTR_ODBC_VERSION, reinterpret_cast<void*>(SQL_OV_ODBC3), 0);
415 testResult(result, SQL_HANDLE_ENV, m_environment, "Could not set the ODBC version.");
4f983d1b
PD
416
417 // Allocate connection handle.
ff05c7e1
O
418 result = SQLAllocHandle(SQL_HANDLE_DBC, m_environment, &m_connection);
419 testResult(result, SQL_HANDLE_ENV, m_environment, "Could not allocate a connection handle.");
4f983d1b
PD
420
421 // Connect to the database.
ff05c7e1
O
422 char* l_dsn = strdup(dsn.c_str());
423 char* l_username = strdup(username.c_str());
424 char* l_password = strdup(password.c_str());
4f983d1b 425
ff05c7e1 426 result = SQLConnect(m_connection,
7ca490f0
O
427 reinterpret_cast<SQLTCHAR*>(l_dsn), dsn.length(),
428 reinterpret_cast<SQLTCHAR*>(l_username), username.length(),
429 reinterpret_cast<SQLTCHAR*>(l_password), password.length());
4f983d1b 430
ff05c7e1
O
431 free(l_dsn);
432 free(l_username);
433 free(l_password);
4f983d1b 434
ff05c7e1 435 testResult(result, SQL_HANDLE_DBC, m_connection, "Could not connect to ODBC datasource.");
4f983d1b 436
ff05c7e1
O
437 m_busy = false;
438 m_log = false;
4f983d1b
PD
439}
440
4f983d1b 441// Destructor.
df8248ca 442SODBC::~SODBC()
4f983d1b
PD
443{
444 // Disconnect from database and free all used resources.
445 // SQLFreeHandle( SQL_HANDLE_STMT, m_statement );
446
ff05c7e1 447 SQLDisconnect(m_connection);
4f983d1b 448
ff05c7e1
O
449 SQLFreeHandle(SQL_HANDLE_DBC, m_connection);
450 SQLFreeHandle(SQL_HANDLE_ENV, m_environment);
4f983d1b
PD
451
452 // Free all allocated column memory.
453 // for ( int i = 0; i < m_columnInfo.size(); i++ )
454 // {
455 // if ( m_columnInfo[ i ].m_pData )
456 // delete m_columnInfo[ i ].m_pData;
457 // }
458}
459
460// Executes a command.
ff05c7e1 461void SODBC::execute(const std::string& command)
4f983d1b 462{
d2bc6b27 463 SODBCStatement stmt(command, m_log, 0, m_connection);
4f983d1b
PD
464
465 stmt.execute()->reset();
466}
467
468// Sets the log state.
ff05c7e1 469void SODBC::setLog(bool state)
4f983d1b
PD
470{
471 m_log = state;
472}
473
474// Returns an exception.
ff05c7e1 475SSqlException SODBC::sPerrorException(const std::string& reason)
4f983d1b 476{
ff05c7e1 477 return SSqlException(reason);
4f983d1b
PD
478}
479
a59a9c23 480std::unique_ptr<SSqlStatement> SODBC::prepare(const string& query, int nparams)
4f983d1b 481{
2bbc9eb0 482 return std::make_unique<SODBCStatement>(query, m_log, nparams, m_connection);
4f983d1b
PD
483}
484
ff05c7e1
O
485void SODBC::startTransaction()
486{
4f983d1b
PD
487 // cerr<<"starting transaction"<<endl;
488 SQLRETURN result;
489 result = SQLSetConnectAttr(m_connection, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
ff05c7e1 490 testResult(result, SQL_HANDLE_DBC, m_connection, "startTransaction (enable autocommit) failed");
4f983d1b
PD
491}
492
ff05c7e1
O
493void SODBC::commit()
494{
4f983d1b
PD
495 // cerr<<"commit!"<<endl;
496 SQLRETURN result;
497
498 result = SQLEndTran(SQL_HANDLE_DBC, m_connection, SQL_COMMIT); // don't really need this, AUTOCOMMIT_OFF below will also commit
ff05c7e1 499 testResult(result, SQL_HANDLE_DBC, m_connection, "commit failed");
4f983d1b
PD
500
501 result = SQLSetConnectAttr(m_connection, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
ff05c7e1 502 testResult(result, SQL_HANDLE_DBC, m_connection, "disabling autocommit after commit failed");
4f983d1b
PD
503}
504
ff05c7e1
O
505void SODBC::rollback()
506{
4f983d1b
PD
507 // cerr<<"rollback!"<<endl;
508 SQLRETURN result;
509
510 result = SQLEndTran(SQL_HANDLE_DBC, m_connection, SQL_ROLLBACK);
ff05c7e1 511 testResult(result, SQL_HANDLE_DBC, m_connection, "rollback failed");
4f983d1b
PD
512
513 result = SQLSetConnectAttr(m_connection, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
ff05c7e1 514 testResult(result, SQL_HANDLE_DBC, m_connection, "disabling autocommit after rollback failed");
4f983d1b 515}
21143435 516
ff05c7e1
O
517void SODBC::testResult(SQLRETURN result, SQLSMALLINT type, SQLHANDLE handle, const std::string& message)
518{
21143435 519 std::string errorMessage;
ff05c7e1
O
520 if (!realTestResult(result, type, handle, message, errorMessage))
521 throw SSqlException(errorMessage);
21143435 522}