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

PowerDNS / pdns / 18370591226

09 Oct 2025 08:40AM UTC coverage: 64.094% (-0.04%) from 64.136%
18370591226

Pull #16224

github

web-flow
Merge b58891300 into 152db0df0
Pull Request #16224: dnsdist: Fix a typo in the XSK documentation

42757 of 101504 branches covered (42.12%)

Branch coverage included in aggregate %.

129859 of 167814 relevant lines covered (77.38%)

5755713.48 hits per line

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

70.08
/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,710,447✔
30
  // cerr<<"result = "<<result<<endl;
31
  if (result == SQL_SUCCESS || result == SQL_SUCCESS_WITH_INFO)
1,710,447!
32
    return true;
1,710,447✔
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
  {
58,875✔
70
    d_query = query;
58,875✔
71
    d_conn = connection;
58,875✔
72
    d_dolog = dolog;
58,875✔
73
    d_residx = 0;
58,875✔
74
    d_paridx = 0;
58,875✔
75
    d_result = SQL_NO_DATA;
58,875✔
76
    d_statement = NULL;
58,875✔
77
    d_prepared = false;
58,875✔
78
    m_columncount = 0;
58,875✔
79
    d_parnum = nparams;
58,875✔
80
  }
58,875✔
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
  {
265,571✔
94
    prepareStatement();
265,571✔
95
    d_req_bind.push_back(p);
265,571✔
96
    SQLLEN ColumnSize = (p.ParameterType == SQL_VARCHAR) ? *(p.LenPtr) : 0;
265,571✔
97
    SQLRETURN result = SQLBindParameter(
265,571✔
98
      d_statement, // StatementHandle,
265,571✔
99
      d_paridx + 1, // ParameterNumber,
265,571✔
100
      SQL_PARAM_INPUT, // InputOutputType,
265,571✔
101
      p.ValueType, // ValueType,
265,571✔
102
      p.ParameterType, // ParameterType,
265,571✔
103
      ColumnSize, // ColumnSize,
265,571✔
104
      0, // DecimalDigits,
265,571✔
105
      p.ParameterValuePtr, // ParameterValuePtr,
265,571✔
106
      0, // BufferLength,
265,571✔
107
      p.LenPtr // StrLen_or_IndPtr
265,571✔
108
    );
265,571✔
109
    testResult(result, SQL_HANDLE_STMT, d_statement, "Could not bind parameter.");
265,571✔
110
    d_paridx++;
265,571✔
111

112
    return this;
265,571✔
113
  }
265,571✔
114

115
  SSqlStatement* bind(const string& name, bool value) override
116
  {
61,656✔
117
    prepareStatement();
61,656✔
118
    return bind(name, (uint32_t)value);
61,656✔
119
  }
61,656✔
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
  {
70,282✔
129
    prepareStatement();
70,282✔
130
    return bind(name, (uint32_t)value);
70,282✔
131
  }
70,282✔
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
  {
132,074✔
141
    prepareStatement();
132,074✔
142
    ODBCParam p;
132,074✔
143
    // NOLINTBEGIN(cppcoreguidelines-owning-memory)
144
    p.ParameterValuePtr = new UDWORD{value};
132,074✔
145
    p.LenPtr = new SQLLEN{sizeof(UDWORD)};
132,074✔
146
    // NOLINTEND(cppcoreguidelines-owning-memory)
147
    p.ParameterType = SQL_INTEGER;
132,074✔
148
    p.ValueType = SQL_INTEGER;
132,074✔
149
    return bind(name, p);
132,074✔
150
  }
132,074✔
151

152
  SSqlStatement* bind(const string& name, unsigned long value) override
153
  {
×
154
    prepareStatement();
×
155
    ODBCParam p;
×
156
    // NOLINTBEGIN(cppcoreguidelines-owning-memory)
157
    p.ParameterValuePtr = new ULONG{value};
×
158
    p.LenPtr = new SQLLEN{sizeof(ULONG)};
×
159
    // NOLINTEND(cppcoreguidelines-owning-memory)
160
    p.ParameterType = SQL_INTEGER;
×
161
    p.ValueType = SQL_INTEGER;
×
162
    return bind(name, p);
×
163
  }
×
164

165
  SSqlStatement* bind(const string& name, unsigned long long value) override
166
  {
×
167
    prepareStatement();
×
168
    ODBCParam p;
×
169
    // NOLINTBEGIN(cppcoreguidelines-owning-memory)
170
    p.ParameterValuePtr = new unsigned long long{value};
×
171
    p.LenPtr = new SQLLEN{sizeof(unsigned long long)};
×
172
    // NOLINTEND(cppcoreguidelines-owning-memory)
173
    p.ParameterType = SQL_BIGINT;
×
174
    p.ValueType = SQL_C_UBIGINT;
×
175
    return bind(name, p);
×
176
  }
×
177

178
  SSqlStatement* bind(const string& name, const std::string& value) override
179
  {
133,222✔
180

181
    // cerr<<"asked to bind string "<<value<<endl;
182

183
    if (d_req_bind.size() > (d_parnum + 1))
133,222!
184
      throw SSqlException("Trying to bind too many parameters.");
×
185
    prepareStatement();
133,222✔
186
    ODBCParam p;
133,222✔
187

188
    // NOLINTBEGIN(cppcoreguidelines-owning-memory)
189
    p.ParameterValuePtr = (char*)new char[value.size() + 1];
133,222✔
190
    value.copy((char*)p.ParameterValuePtr, value.size());
133,222✔
191
    ((char*)p.ParameterValuePtr)[value.size()] = 0;
133,222✔
192
    p.LenPtr = new SQLLEN{static_cast<SQLLEN>(value.size())};
133,222✔
193
    // NOLINTEND(cppcoreguidelines-owning-memory)
194
    p.ParameterType = SQL_VARCHAR;
133,222✔
195
    p.ValueType = SQL_C_CHAR;
133,222✔
196

197
    return bind(name, p);
133,222✔
198
  }
133,222✔
199

200
  SSqlStatement* bindNull(const string& name) override
201
  {
276✔
202
    if (d_req_bind.size() > (d_parnum + 1))
276!
203
      throw SSqlException("Trying to bind too many parameters.");
×
204

205
    prepareStatement();
276✔
206
    ODBCParam p;
276✔
207

208
    p.ParameterValuePtr = NULL;
276✔
209
    // NOLINTBEGIN(cppcoreguidelines-owning-memory)
210
    p.LenPtr = new SQLLEN{SQL_NULL_DATA};
276✔
211
    // NOLINTEND(cppcoreguidelines-owning-memory)
212
    p.ParameterType = SQL_VARCHAR;
276✔
213
    p.ValueType = SQL_C_CHAR;
276✔
214

215
    return bind(name, p);
276✔
216
  }
276✔
217

218
  SSqlStatement* execute() override
219
  {
71,885✔
220
    prepareStatement();
71,885✔
221
    SQLRETURN result;
71,885✔
222
    // cerr<<"execute("<<d_query<<")"<<endl;
223
    if (d_dolog) {
71,885!
224
      g_log << Logger::Warning << "Query: " << d_query << endl;
×
225
    }
×
226

227
    result = SQLExecute(d_statement);
71,885✔
228
    if (result != SQL_NO_DATA) // odbc+sqlite returns this on 'no rows updated'
71,885✔
229
      testResult(result, SQL_HANDLE_STMT, d_statement, "Could not execute query (" + d_query + ").");
71,873✔
230

231
    // Determine the number of columns.
232
    result = SQLNumResultCols(d_statement, &m_columncount);
71,885✔
233
    testResult(result, SQL_HANDLE_STMT, d_statement, "Could not determine the number of columns.");
71,885✔
234
    // cerr<<"got "<<m_columncount<<" columns"<<endl;
235

236
    if (m_columncount) {
71,885✔
237
      // cerr<<"first SQLFetch"<<endl;
238
      d_result = SQLFetch(d_statement);
10,013✔
239
      // cerr<<"first SQLFetch done, d_result="<<d_result<<endl;
240
    }
10,013✔
241
    else
61,872✔
242
      d_result = SQL_NO_DATA;
61,872✔
243

244
    if (d_result != SQL_NO_DATA)
71,885✔
245
      testResult(d_result, SQL_HANDLE_STMT, d_statement, "Could not do first SQLFetch for (" + d_query + ").");
8,753✔
246
    return this;
71,885✔
247
  }
71,885✔
248

249
  bool hasNextRow() override
250
  {
143,606✔
251
    // cerr<<"hasNextRow d_result="<<d_result<<endl;
252
    return d_result != SQL_NO_DATA;
143,606✔
253
  }
143,606✔
254
  SSqlStatement* nextRow(row_t& row) override;
255

256
  SSqlStatement* getResult(result_t& result) override
257
  {
269✔
258
    result.clear();
269✔
259
    // if (d_res == NULL) return this;
260
    row_t row;
269✔
261
    while (hasNextRow()) {
520✔
262
      nextRow(row);
251✔
263
      result.push_back(row);
251✔
264
    }
251✔
265
    return this;
269✔
266
  }
269✔
267

268
  SSqlStatement* reset() override
269
  {
125,945✔
270
    SQLCloseCursor(d_statement); // hack, this probably violates some state transitions
125,945✔
271

272
    for (auto& i : d_req_bind) {
269,219✔
273
      // NOLINTBEGIN(cppcoreguidelines-owning-memory)
274
      if (i.ParameterType == SQL_VARCHAR) {
265,572✔
275
        delete[] static_cast<char*>(i.ParameterValuePtr);
133,498✔
276
      }
133,498✔
277
      else if (i.ParameterType == SQL_INTEGER) {
132,074!
278
        if (*i.LenPtr == sizeof(UDWORD)) {
132,074!
279
          delete static_cast<UDWORD*>(i.ParameterValuePtr);
132,074✔
280
        }
132,074✔
281
        else {
×
282
          delete static_cast<ULONG*>(i.ParameterValuePtr);
×
283
        }
×
284
      }
132,074✔
285
      else if (i.ParameterType == SQL_C_UBIGINT) {
×
286
        delete static_cast<unsigned long long*>(i.ParameterValuePtr);
×
287
      }
×
288
      // NOLINTEND(cppcoreguidelines-owning-memory)
289

290
      delete i.LenPtr;
265,572✔
291
    }
265,572✔
292
    d_req_bind.clear();
125,945✔
293
    d_residx = 0;
125,945✔
294
    d_paridx = 0;
125,945✔
295
    return this;
125,945✔
296
  }
125,945✔
297
  const std::string& getQuery() override { return d_query; }
×
298

299
  ~SODBCStatement() override
300
  {
53,888✔
301
    releaseStatement();
53,888✔
302
  }
53,888✔
303

304
private:
305
  void testResult(SQLRETURN result, SQLSMALLINT type, SQLHANDLE handle, const std::string& message)
306
  {
1,706,275✔
307
    std::string errorMessage;
1,706,275✔
308
    if (!realTestResult(result, type, handle, message, errorMessage)) {
1,706,275!
309
      releaseStatement();
×
310
      throw SSqlException(errorMessage);
×
311
    }
×
312
  }
1,706,275✔
313

314
  void releaseStatement()
315
  {
54,043✔
316
    reset();
54,043✔
317
    if (d_statement != NULL)
54,043✔
318
      SQLFreeHandle(SQL_HANDLE_STMT, d_statement);
2,026✔
319
    d_prepared = false;
54,043✔
320
  }
54,043✔
321

322
  void prepareStatement()
323
  {
734,965✔
324
    if (d_prepared)
734,965✔
325
      return;
732,729✔
326

327
    SQLRETURN result;
2,236✔
328

329
    // Allocate statement handle.
330
    result = SQLAllocHandle(SQL_HANDLE_STMT, d_conn, &d_statement);
2,236✔
331
    testResult(result, SQL_HANDLE_DBC, d_conn, "Could not allocate a statement handle.");
2,236✔
332

333
    result = SQLPrepare(d_statement, (SQLCHAR*)d_query.c_str(), SQL_NTS);
2,236✔
334
    testResult(result, SQL_HANDLE_STMT, d_statement, "Could not prepare query.");
2,236✔
335

336
    SQLSMALLINT paramcount;
2,236✔
337
    result = SQLNumParams(d_statement, &paramcount);
2,236✔
338
    testResult(result, SQL_HANDLE_STMT, d_statement, "Could not get parameter count.");
2,236✔
339

340
    if (paramcount != static_cast<SQLSMALLINT>(d_parnum)) {
2,236!
341
      releaseStatement();
×
342
      throw SSqlException("Provided parameter count does not match statement: " + d_query);
×
343
    }
×
344

345
    // cerr<<"prepared ("<<query<<")"<<endl;
346
    d_prepared = true;
2,236✔
347
  }
2,236✔
348

349
  string d_query;
350
  bool d_dolog;
351
  bool d_prepared;
352
  int d_residx;
353
  size_t d_paridx, d_parnum;
354
  SQLRETURN d_result;
355

356
  SQLHDBC d_conn;
357
  SQLHSTMT d_statement; //!< Database statement handle.
358

359
  //! Column type.
360
  struct column_t
361
  {
362
    SQLSMALLINT m_type; //!< Type of the column.
363
    SQLULEN m_size; //!< Column size.
364
    SQLPOINTER m_pData; //!< Pointer to the memory where to store the data.
365
    bool m_canBeNull; //!< Can this column be null?
366
  };
367

368
  //! Column info.
369
  SQLSMALLINT m_columncount;
370
};
371

372
SSqlStatement* SODBCStatement::nextRow(row_t& row)
373
{
133,593✔
374
  SQLRETURN result;
133,593✔
375

376
  row.clear();
133,593✔
377

378
  result = d_result;
133,593✔
379
  // cerr<<"at start of nextRow, previous SQLFetch result is "<<result<<endl;
380
  // FIXME handle errors (SQL_NO_DATA==100, anything other than the two SUCCESS options below is bad news)
381
  if (result == SQL_SUCCESS || result == SQL_SUCCESS_WITH_INFO) {
133,593!
382
    // cerr<<"got row"<<endl;
383
    // We've got a data row, now lets get the results.
384
    for (int i = 0; i < m_columncount; i++) {
1,281,484✔
385
      SQLLEN len;
1,147,891✔
386
      SQLCHAR coldata[128 * 1024];
1,147,891✔
387
      std::string strres = "";
1,147,891✔
388
      result = SQLGetData(d_statement, i + 1, SQL_C_CHAR, (SQLPOINTER)coldata, sizeof(coldata), &len);
1,147,891✔
389
      testResult(result, SQL_HANDLE_STMT, d_statement, "Could not get data.");
1,147,891✔
390
      if (len > SQL_NULL_DATA)
1,147,891✔
391
        strres = std::string(reinterpret_cast<const char*>(coldata), std::min<SQLLEN>(sizeof(coldata) - 1, len)); // do not use nil byte
1,021,421✔
392
      row.push_back(strres);
1,147,891✔
393
    }
1,147,891✔
394

395
    // Done!
396
    d_residx++;
133,593✔
397
    // cerr<<"SQLFetch"<<endl;
398
    d_result = SQLFetch(d_statement);
133,593✔
399
    // cerr<<"subsequent SQLFetch done, d_result="<<d_result<<endl;
400
    if (d_result == SQL_NO_DATA) {
133,593✔
401
      SQLRETURN result2 = SQLMoreResults(d_statement);
8,753✔
402
      // cerr<<"SQLMoreResults done, result="<<d_result2<<endl;
403
      if (result2 == SQL_NO_DATA) {
8,753!
404
        d_result = result2;
8,753✔
405
      }
8,753✔
406
      else {
×
407
        testResult(result2, SQL_HANDLE_STMT, d_statement, "Could not fetch next result set for (" + d_query + ").");
×
408
        d_result = SQLFetch(d_statement);
×
409
      }
×
410
    }
8,753✔
411
    testResult(result, SQL_HANDLE_STMT, d_statement, "Could not do subsequent SQLFetch for (" + d_query + ").");
133,593✔
412

413
    return this;
133,593✔
414
  }
133,593✔
415

416
  SQLFreeStmt(d_statement, SQL_CLOSE);
×
417
  throw SSqlException("Should not get here.");
×
418
}
133,593✔
419

420
// Constructor.
421
SODBC::SODBC(
422
  const std::string& dsn,
423
  const std::string& username,
424
  const std::string& password)
425
{
866✔
426
  SQLRETURN result;
866✔
427

428
  // Allocate an environment handle.
429
  result = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &m_environment);
866✔
430
  testResult(result, SQL_NULL_HANDLE, NULL, "Could not allocate an environment handle.");
866✔
431

432
  // Set ODBC version. (IEUW!)
433
  result = SQLSetEnvAttr(m_environment, SQL_ATTR_ODBC_VERSION, reinterpret_cast<void*>(SQL_OV_ODBC3), 0);
866✔
434
  testResult(result, SQL_HANDLE_ENV, m_environment, "Could not set the ODBC version.");
866✔
435

436
  // Allocate connection handle.
437
  result = SQLAllocHandle(SQL_HANDLE_DBC, m_environment, &m_connection);
866✔
438
  testResult(result, SQL_HANDLE_ENV, m_environment, "Could not allocate a connection handle.");
866✔
439

440
  // Connect to the database.
441
  char* l_dsn = strdup(dsn.c_str());
866✔
442
  char* l_username = strdup(username.c_str());
866✔
443
  char* l_password = strdup(password.c_str());
866✔
444

445
  result = SQLConnect(m_connection,
866✔
446
                      reinterpret_cast<SQLTCHAR*>(l_dsn), dsn.length(),
866✔
447
                      reinterpret_cast<SQLTCHAR*>(l_username), username.length(),
866✔
448
                      reinterpret_cast<SQLTCHAR*>(l_password), password.length());
866✔
449

450
  free(l_dsn);
866✔
451
  free(l_username);
866✔
452
  free(l_password);
866✔
453

454
  testResult(result, SQL_HANDLE_DBC, m_connection, "Could not connect to ODBC datasource.");
866✔
455

456
  m_busy = false;
866✔
457
  m_log = false;
866✔
458
}
866✔
459

460
// Destructor.
461
SODBC::~SODBC()
462
{
804✔
463
  // Disconnect from database and free all used resources.
464
  // SQLFreeHandle( SQL_HANDLE_STMT, m_statement );
465

466
  SQLDisconnect(m_connection);
804✔
467

468
  SQLFreeHandle(SQL_HANDLE_DBC, m_connection);
804✔
469
  SQLFreeHandle(SQL_HANDLE_ENV, m_environment);
804✔
470

471
  // Free all allocated column memory.
472
  // for ( int i = 0; i < m_columnInfo.size(); i++ )
473
  // {
474
  //   if ( m_columnInfo[ i ].m_pData )
475
  //     delete m_columnInfo[ i ].m_pData;
476
  // }
477
}
804✔
478

479
// Executes a command.
480
void SODBC::execute(const std::string& command)
481
{
×
482
  SODBCStatement stmt(command, m_log, 0, m_connection);
×
483

484
  stmt.execute()->reset();
×
485
}
×
486

487
// Sets the log state.
488
void SODBC::setLog(bool state)
489
{
866✔
490
  m_log = state;
866✔
491
}
866✔
492

493
// Returns an exception.
494
SSqlException SODBC::sPerrorException(const std::string& reason)
495
{
×
496
  return SSqlException(reason);
×
497
}
×
498

499
std::unique_ptr<SSqlStatement> SODBC::prepare(const string& query, int nparams)
500
{
58,887✔
501
  return std::make_unique<SODBCStatement>(query, m_log, nparams, m_connection);
58,887✔
502
}
58,887✔
503

504
void SODBC::startTransaction()
505
{
244✔
506
  // cerr<<"starting transaction"<<endl;
507
  SQLRETURN result;
244✔
508
  result = SQLSetConnectAttr(m_connection, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
244✔
509
  testResult(result, SQL_HANDLE_DBC, m_connection, "startTransaction (enable autocommit) failed");
244✔
510
}
244✔
511

512
void SODBC::commit()
513
{
232✔
514
  // cerr<<"commit!"<<endl;
515
  SQLRETURN result;
232✔
516

517
  result = SQLEndTran(SQL_HANDLE_DBC, m_connection, SQL_COMMIT); // don't really need this, AUTOCOMMIT_OFF below will also commit
232✔
518
  testResult(result, SQL_HANDLE_DBC, m_connection, "commit failed");
232✔
519

520
  result = SQLSetConnectAttr(m_connection, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
232✔
521
  testResult(result, SQL_HANDLE_DBC, m_connection, "disabling autocommit after commit failed");
232✔
522
}
232✔
523

524
void SODBC::rollback()
525
{
×
526
  // cerr<<"rollback!"<<endl;
527
  SQLRETURN result;
×
528

529
  result = SQLEndTran(SQL_HANDLE_DBC, m_connection, SQL_ROLLBACK);
×
530
  testResult(result, SQL_HANDLE_DBC, m_connection, "rollback failed");
×
531

532
  result = SQLSetConnectAttr(m_connection, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
×
533
  testResult(result, SQL_HANDLE_DBC, m_connection, "disabling autocommit after rollback failed");
×
534
}
×
535

536
void SODBC::testResult(SQLRETURN result, SQLSMALLINT type, SQLHANDLE handle, const std::string& message)
537
{
4,172✔
538
  std::string errorMessage;
4,172✔
539
  if (!realTestResult(result, type, handle, message, errorMessage))
4,172!
540
    throw SSqlException(errorMessage);
×
541
}
4,172✔
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

© 2025 Coveralls, Inc