• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PowerDNS / pdns / 12178254343

05 Dec 2024 10:51AM UTC coverage: 64.385% (-0.3%) from 64.734%
12178254343

push

github

web-flow
Merge pull request #14934 from omoerbeek/gh-action-build-name

GH workflow for building packages: change name so target isn't chopped off

37416 of 88740 branches covered (42.16%)

Branch coverage included in aggregate %.

125826 of 164800 relevant lines covered (76.35%)

3849962.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.8
/modules/godbcbackend/sodbc.cc
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
 */
22
#include "pdns/logger.hh"
23
#include "pdns/utility.hh"
24
#include <sstream>
25
#include "sodbc.hh"
26
#include <string.h>
27

28
static bool realTestResult(SQLRETURN result, SQLSMALLINT type, SQLHANDLE handle, const std::string& message, std::string& errorMessage)
29
{
1,699,250✔
30
  // cerr<<"result = "<<result<<endl;
31
  if (result == SQL_SUCCESS || result == SQL_SUCCESS_WITH_INFO)
1,699,250!
32
    return true;
1,699,250✔
33

34
  ostringstream errmsg;
×
35

36
  errmsg << message << ": ";
×
37

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;
×
41
    errorMessage = errmsg.str();
×
42
    return false;
×
43
  }
×
44

45
  SQLINTEGER i = 0;
×
46
  SQLINTEGER native;
×
47
  SQLCHAR state[7];
×
48
  SQLCHAR text[256];
×
49
  SQLSMALLINT len;
×
50
  SQLRETURN ret;
×
51

52
  do {
×
53
    // cerr<<"getting sql diag record "<<i<<endl;
54
    ret = SQLGetDiagRec(type, handle, ++i, state, &native, text,
×
55
                        sizeof(text), &len);
×
56
    // cerr<<"getdiagrec said "<<ret<<endl;
57
    if (SQL_SUCCEEDED(ret)) { // cerr<<"got it"<<endl;
×
58
      errmsg << state << i << native << text << "/";
×
59
    }
×
60
  } while (ret == SQL_SUCCESS);
×
61
  errorMessage = errmsg.str();
×
62
  return false;
×
63
}
×
64

65
class SODBCStatement : public SSqlStatement
66
{
67
public:
68
  SODBCStatement(const string& query, bool dolog, int nparams, SQLHDBC connection)
69
  {
52,388✔
70
    d_query = query;
52,388✔
71
    d_conn = connection;
52,388✔
72
    d_dolog = dolog;
52,388✔
73
    d_residx = 0;
52,388✔
74
    d_paridx = 0;
52,388✔
75
    d_result = SQL_NO_DATA;
52,388✔
76
    d_statement = NULL;
52,388✔
77
    d_prepared = false;
52,388✔
78
    m_columncount = 0;
52,388✔
79
    d_parnum = nparams;
52,388✔
80
  }
52,388✔
81

82
  struct ODBCParam
83
  {
84
    SQLPOINTER ParameterValuePtr;
85
    SQLLEN* LenPtr;
86
    SQLSMALLINT ParameterType;
87
    SQLSMALLINT ValueType;
88
  };
89

90
  vector<ODBCParam> d_req_bind;
91

92
  SSqlStatement* bind(const string& /* name */, ODBCParam& p)
93
  {
263,308✔
94
    prepareStatement();
263,308✔
95
    d_req_bind.push_back(p);
263,308✔
96
    SQLLEN ColumnSize = (p.ParameterType == SQL_VARCHAR) ? *(p.LenPtr) : 0;
263,308✔
97
    SQLRETURN result = SQLBindParameter(
263,308✔
98
      d_statement, // StatementHandle,
263,308✔
99
      d_paridx + 1, // ParameterNumber,
263,308✔
100
      SQL_PARAM_INPUT, // InputOutputType,
263,308✔
101
      p.ValueType, // ValueType,
263,308✔
102
      p.ParameterType, // ParameterType,
263,308✔
103
      ColumnSize, // ColumnSize,
263,308✔
104
      0, // DecimalDigits,
263,308✔
105
      p.ParameterValuePtr, // ParameterValuePtr,
263,308✔
106
      0, // BufferLength,
263,308✔
107
      p.LenPtr // StrLen_or_IndPtr
263,308✔
108
    );
263,308✔
109
    testResult(result, SQL_HANDLE_STMT, d_statement, "Could not bind parameter.");
263,308✔
110
    d_paridx++;
263,308✔
111

112
    return this;
263,308✔
113
  }
263,308✔
114

115
  SSqlStatement* bind(const string& name, bool value) override
116
  {
61,551✔
117
    prepareStatement();
61,551✔
118
    return bind(name, (uint32_t)value);
61,551✔
119
  }
61,551✔
120

121
  SSqlStatement* bind(const string& name, long value) override
122
  {
×
123
    prepareStatement();
×
124
    return bind(name, (unsigned long)value);
×
125
  }
×
126

127
  SSqlStatement* bind(const string& name, int value) override
128
  {
2,152✔
129
    prepareStatement();
2,152✔
130
    return bind(name, (uint32_t)value);
2,152✔
131
  }
2,152✔
132

133
  SSqlStatement* bind(const string& name, long long value) override
134
  {
×
135
    prepareStatement();
×
136
    return bind(name, (unsigned long long)value);
×
137
  }
×
138

139
  SSqlStatement* bind(const string& name, uint32_t value) override
140
  {
131,101✔
141
    prepareStatement();
131,101✔
142
    ODBCParam p;
131,101✔
143
    p.ParameterValuePtr = new UDWORD{value};
131,101✔
144
    p.LenPtr = new SQLLEN{sizeof(UDWORD)};
131,101✔
145
    p.ParameterType = SQL_INTEGER;
131,101✔
146
    p.ValueType = SQL_INTEGER;
131,101✔
147
    return bind(name, p);
131,101✔
148
  }
131,101✔
149

150
  SSqlStatement* bind(const string& name, unsigned long value) override
151
  {
×
152
    prepareStatement();
×
153
    ODBCParam p;
×
154
    p.ParameterValuePtr = new ULONG{value};
×
155
    p.LenPtr = new SQLLEN{sizeof(ULONG)};
×
156
    p.ParameterType = SQL_INTEGER;
×
157
    p.ValueType = SQL_INTEGER;
×
158
    return bind(name, p);
×
159
  }
×
160

161
  SSqlStatement* bind(const string& name, unsigned long long value) override
162
  {
×
163
    prepareStatement();
×
164
    ODBCParam p;
×
165
    p.ParameterValuePtr = new unsigned long long{value};
×
166
    p.LenPtr = new SQLLEN{sizeof(unsigned long long)};
×
167
    p.ParameterType = SQL_BIGINT;
×
168
    p.ValueType = SQL_C_UBIGINT;
×
169
    return bind(name, p);
×
170
  }
×
171

172
  SSqlStatement* bind(const string& name, const std::string& value) override
173
  {
131,979✔
174

175
    // cerr<<"asked to bind string "<<value<<endl;
176

177
    if (d_req_bind.size() > (d_parnum + 1))
131,979!
178
      throw SSqlException("Trying to bind too many parameters.");
×
179
    prepareStatement();
131,979✔
180
    ODBCParam p;
131,979✔
181

182
    p.ParameterValuePtr = (char*)new char[value.size() + 1];
131,979✔
183
    value.copy((char*)p.ParameterValuePtr, value.size());
131,979✔
184
    ((char*)p.ParameterValuePtr)[value.size()] = 0;
131,979✔
185
    p.LenPtr = new SQLLEN;
131,979✔
186
    *(p.LenPtr) = value.size();
131,979✔
187
    p.ParameterType = SQL_VARCHAR;
131,979✔
188
    p.ValueType = SQL_C_CHAR;
131,979✔
189

190
    return bind(name, p);
131,979✔
191
  }
131,979✔
192

193
  SSqlStatement* bindNull(const string& name) override
194
  {
228✔
195
    if (d_req_bind.size() > (d_parnum + 1))
228!
196
      throw SSqlException("Trying to bind too many parameters.");
×
197

198
    prepareStatement();
228✔
199
    ODBCParam p;
228✔
200

201
    p.ParameterValuePtr = NULL;
228✔
202
    p.LenPtr = new SQLLEN;
228✔
203
    *(p.LenPtr) = SQL_NULL_DATA;
228✔
204
    p.ParameterType = SQL_VARCHAR;
228✔
205
    p.ValueType = SQL_C_CHAR;
228✔
206

207
    return bind(name, p);
228✔
208
  }
228✔
209

210
  SSqlStatement* execute() override
211
  {
70,957✔
212
    prepareStatement();
70,957✔
213
    SQLRETURN result;
70,957✔
214
    // cerr<<"execute("<<d_query<<")"<<endl;
215
    if (d_dolog) {
70,957!
216
      g_log << Logger::Warning << "Query: " << d_query << endl;
×
217
    }
×
218

219
    result = SQLExecute(d_statement);
70,957✔
220
    if (result != SQL_NO_DATA) // odbc+sqlite returns this on 'no rows updated'
70,957✔
221
      testResult(result, SQL_HANDLE_STMT, d_statement, "Could not execute query (" + d_query + ").");
70,956✔
222

223
    // Determine the number of columns.
224
    result = SQLNumResultCols(d_statement, &m_columncount);
70,957✔
225
    testResult(result, SQL_HANDLE_STMT, d_statement, "Could not determine the number of columns.");
70,957✔
226
    // cerr<<"got "<<m_columncount<<" columns"<<endl;
227

228
    if (m_columncount) {
70,957✔
229
      // cerr<<"first SQLFetch"<<endl;
230
      d_result = SQLFetch(d_statement);
9,250✔
231
      // cerr<<"first SQLFetch done, d_result="<<d_result<<endl;
232
    }
9,250✔
233
    else
61,707✔
234
      d_result = SQL_NO_DATA;
61,707✔
235

236
    if (d_result != SQL_NO_DATA)
70,957✔
237
      testResult(d_result, SQL_HANDLE_STMT, d_statement, "Could not do first SQLFetch for (" + d_query + ").");
8,098✔
238
    return this;
70,957✔
239
  }
70,957✔
240

241
  bool hasNextRow() override
242
  {
142,030✔
243
    // cerr<<"hasNextRow d_result="<<d_result<<endl;
244
    return d_result != SQL_NO_DATA;
142,030✔
245
  }
142,030✔
246
  SSqlStatement* nextRow(row_t& row) override;
247

248
  SSqlStatement* getResult(result_t& result) override
249
  {
185✔
250
    result.clear();
185✔
251
    // if (d_res == NULL) return this;
252
    row_t row;
185✔
253
    while (hasNextRow()) {
358✔
254
      nextRow(row);
173✔
255
      result.push_back(row);
173✔
256
    }
173✔
257
    return this;
185✔
258
  }
185✔
259

260
  SSqlStatement* reset() override
261
  {
119,401✔
262
    SQLCloseCursor(d_statement); // hack, this probably violates some state transitions
119,401✔
263

264
    for (auto& i : d_req_bind) {
266,185✔
265
      if (i.ParameterType == SQL_VARCHAR)
263,308✔
266
        delete[](char*) i.ParameterValuePtr;
132,207✔
267
      else if (i.ParameterType == SQL_INTEGER)
131,101!
268
        delete (ULONG*)i.ParameterValuePtr;
131,101✔
269
      else if (i.ParameterType == SQL_C_UBIGINT)
×
270
        delete (unsigned long long*)i.ParameterValuePtr;
×
271
      delete i.LenPtr;
263,308✔
272
    }
263,308✔
273
    d_req_bind.clear();
119,401✔
274
    d_residx = 0;
119,401✔
275
    d_paridx = 0;
119,401✔
276
    return this;
119,401✔
277
  }
119,401✔
278
  const std::string& getQuery() override { return d_query; }
×
279

280
  ~SODBCStatement() override
281
  {
48,444✔
282
    releaseStatement();
48,444✔
283
  }
48,444✔
284

285
private:
286
  void testResult(SQLRETURN result, SQLSMALLINT type, SQLHANDLE handle, const std::string& message)
287
  {
1,695,559✔
288
    std::string errorMessage;
1,695,559✔
289
    if (!realTestResult(result, type, handle, message, errorMessage)) {
1,695,559!
290
      releaseStatement();
×
291
      throw SSqlException(errorMessage);
×
292
    }
×
293
  }
1,695,559✔
294

295
  void releaseStatement()
296
  {
48,444✔
297
    reset();
48,444✔
298
    if (d_statement != NULL)
48,444✔
299
      SQLFreeHandle(SQL_HANDLE_STMT, d_statement);
1,616✔
300
    d_prepared = false;
48,444✔
301
  }
48,444✔
302

303
  void prepareStatement()
304
  {
661,276✔
305
    if (d_prepared)
661,276✔
306
      return;
659,450✔
307

308
    SQLRETURN result;
1,826✔
309

310
    // Allocate statement handle.
311
    result = SQLAllocHandle(SQL_HANDLE_STMT, d_conn, &d_statement);
1,826✔
312
    testResult(result, SQL_HANDLE_DBC, d_conn, "Could not allocate a statement handle.");
1,826✔
313

314
    result = SQLPrepare(d_statement, (SQLCHAR*)d_query.c_str(), SQL_NTS);
1,826✔
315
    testResult(result, SQL_HANDLE_STMT, d_statement, "Could not prepare query.");
1,826✔
316

317
    SQLSMALLINT paramcount;
1,826✔
318
    result = SQLNumParams(d_statement, &paramcount);
1,826✔
319
    testResult(result, SQL_HANDLE_STMT, d_statement, "Could not get parameter count.");
1,826✔
320

321
    if (paramcount != static_cast<SQLSMALLINT>(d_parnum)) {
1,826!
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;
1,826✔
328
  }
1,826✔
329

330
  string d_query;
331
  bool d_dolog;
332
  bool d_prepared;
333
  int d_residx;
334
  size_t d_paridx, d_parnum;
335
  SQLRETURN d_result;
336

337
  SQLHDBC d_conn;
338
  SQLHSTMT d_statement; //!< Database statement handle.
339

340
  //! Column type.
341
  struct column_t
342
  {
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?
347
  };
348

349
  //! Column info.
350
  SQLSMALLINT m_columncount;
351
};
352

353
SSqlStatement* SODBCStatement::nextRow(row_t& row)
354
{
132,780✔
355
  SQLRETURN result;
132,780✔
356

357
  row.clear();
132,780✔
358

359
  result = d_result;
132,780✔
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)
362
  if (result == SQL_SUCCESS || result == SQL_SUCCESS_WITH_INFO) {
132,780!
363
    // cerr<<"got row"<<endl;
364
    // We've got a data row, now lets get the results.
365
    for (int i = 0; i < m_columncount; i++) {
1,276,762✔
366
      SQLLEN len;
1,143,982✔
367
      SQLCHAR coldata[128 * 1024];
1,143,982✔
368
      std::string strres = "";
1,143,982✔
369
      result = SQLGetData(d_statement, i + 1, SQL_C_CHAR, (SQLPOINTER)coldata, sizeof(coldata), &len);
1,143,982✔
370
      testResult(result, SQL_HANDLE_STMT, d_statement, "Could not get data.");
1,143,982✔
371
      if (len > SQL_NULL_DATA)
1,143,982✔
372
        strres = std::string(reinterpret_cast<const char*>(coldata), std::min<SQLLEN>(sizeof(coldata) - 1, len)); // do not use nil byte
1,017,877✔
373
      row.push_back(strres);
1,143,982✔
374
    }
1,143,982✔
375

376
    // Done!
377
    d_residx++;
132,780✔
378
    // cerr<<"SQLFetch"<<endl;
379
    d_result = SQLFetch(d_statement);
132,780✔
380
    // cerr<<"subsequent SQLFetch done, d_result="<<d_result<<endl;
381
    if (d_result == SQL_NO_DATA) {
132,780✔
382
      SQLRETURN result2 = SQLMoreResults(d_statement);
8,098✔
383
      // cerr<<"SQLMoreResults done, result="<<d_result2<<endl;
384
      if (result2 == SQL_NO_DATA) {
8,098!
385
        d_result = result2;
8,098✔
386
      }
8,098✔
387
      else {
×
388
        testResult(result2, SQL_HANDLE_STMT, d_statement, "Could not fetch next result set for (" + d_query + ").");
×
389
        d_result = SQLFetch(d_statement);
×
390
      }
×
391
    }
8,098✔
392
    testResult(result, SQL_HANDLE_STMT, d_statement, "Could not do subsequent SQLFetch for (" + d_query + ").");
132,780✔
393

394
    return this;
132,780✔
395
  }
132,780✔
396

397
  SQLFreeStmt(d_statement, SQL_CLOSE);
×
398
  throw SSqlException("Should not get here.");
×
399
}
132,780✔
400

401
// Constructor.
402
SODBC::SODBC(
403
  const std::string& dsn,
404
  const std::string& username,
405
  const std::string& password)
406
{
794✔
407
  SQLRETURN result;
794✔
408

409
  // Allocate an environment handle.
410
  result = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &m_environment);
794✔
411
  testResult(result, SQL_NULL_HANDLE, NULL, "Could not allocate an environment handle.");
794✔
412

413
  // Set ODBC version. (IEUW!)
414
  result = SQLSetEnvAttr(m_environment, SQL_ATTR_ODBC_VERSION, reinterpret_cast<void*>(SQL_OV_ODBC3), 0);
794✔
415
  testResult(result, SQL_HANDLE_ENV, m_environment, "Could not set the ODBC version.");
794✔
416

417
  // Allocate connection handle.
418
  result = SQLAllocHandle(SQL_HANDLE_DBC, m_environment, &m_connection);
794✔
419
  testResult(result, SQL_HANDLE_ENV, m_environment, "Could not allocate a connection handle.");
794✔
420

421
  // Connect to the database.
422
  char* l_dsn = strdup(dsn.c_str());
794✔
423
  char* l_username = strdup(username.c_str());
794✔
424
  char* l_password = strdup(password.c_str());
794✔
425

426
  result = SQLConnect(m_connection,
794✔
427
                      reinterpret_cast<SQLTCHAR*>(l_dsn), dsn.length(),
794✔
428
                      reinterpret_cast<SQLTCHAR*>(l_username), username.length(),
794✔
429
                      reinterpret_cast<SQLTCHAR*>(l_password), password.length());
794✔
430

431
  free(l_dsn);
794✔
432
  free(l_username);
794✔
433
  free(l_password);
794✔
434

435
  testResult(result, SQL_HANDLE_DBC, m_connection, "Could not connect to ODBC datasource.");
794✔
436

437
  m_busy = false;
794✔
438
  m_log = false;
794✔
439
}
794✔
440

441
// Destructor.
442
SODBC::~SODBC()
443
{
734✔
444
  // Disconnect from database and free all used resources.
445
  // SQLFreeHandle( SQL_HANDLE_STMT, m_statement );
446

447
  SQLDisconnect(m_connection);
734✔
448

449
  SQLFreeHandle(SQL_HANDLE_DBC, m_connection);
734✔
450
  SQLFreeHandle(SQL_HANDLE_ENV, m_environment);
734✔
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
}
734✔
459

460
// Executes a command.
461
void SODBC::execute(const std::string& command)
462
{
×
463
  SODBCStatement stmt(command, m_log, 0, m_connection);
×
464

465
  stmt.execute()->reset();
×
466
}
×
467

468
// Sets the log state.
469
void SODBC::setLog(bool state)
470
{
794✔
471
  m_log = state;
794✔
472
}
794✔
473

474
// Returns an exception.
475
SSqlException SODBC::sPerrorException(const std::string& reason)
476
{
×
477
  return SSqlException(reason);
×
478
}
×
479

480
std::unique_ptr<SSqlStatement> SODBC::prepare(const string& query, int nparams)
481
{
52,402✔
482
  return std::make_unique<SODBCStatement>(query, m_log, nparams, m_connection);
52,402✔
483
}
52,402✔
484

485
void SODBC::startTransaction()
486
{
172✔
487
  // cerr<<"starting transaction"<<endl;
488
  SQLRETURN result;
172✔
489
  result = SQLSetConnectAttr(m_connection, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
172✔
490
  testResult(result, SQL_HANDLE_DBC, m_connection, "startTransaction (enable autocommit) failed");
172✔
491
}
172✔
492

493
void SODBC::commit()
494
{
172✔
495
  // cerr<<"commit!"<<endl;
496
  SQLRETURN result;
172✔
497

498
  result = SQLEndTran(SQL_HANDLE_DBC, m_connection, SQL_COMMIT); // don't really need this, AUTOCOMMIT_OFF below will also commit
172✔
499
  testResult(result, SQL_HANDLE_DBC, m_connection, "commit failed");
172✔
500

501
  result = SQLSetConnectAttr(m_connection, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
172✔
502
  testResult(result, SQL_HANDLE_DBC, m_connection, "disabling autocommit after commit failed");
172✔
503
}
172✔
504

505
void SODBC::rollback()
506
{
×
507
  // cerr<<"rollback!"<<endl;
508
  SQLRETURN result;
×
509

510
  result = SQLEndTran(SQL_HANDLE_DBC, m_connection, SQL_ROLLBACK);
×
511
  testResult(result, SQL_HANDLE_DBC, m_connection, "rollback failed");
×
512

513
  result = SQLSetConnectAttr(m_connection, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
×
514
  testResult(result, SQL_HANDLE_DBC, m_connection, "disabling autocommit after rollback failed");
×
515
}
×
516

517
void SODBC::testResult(SQLRETURN result, SQLSMALLINT type, SQLHANDLE handle, const std::string& message)
518
{
3,692✔
519
  std::string errorMessage;
3,692✔
520
  if (!realTestResult(result, type, handle, message, errorMessage))
3,692!
521
    throw SSqlException(errorMessage);
×
522
}
3,692✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc