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

PowerDNS / pdns / 17647386129

11 Sep 2025 02:14PM UTC coverage: 66.018% (-0.02%) from 66.033%
17647386129

push

github

web-flow
Merge pull request #16106 from omoerbeek/update-cargo

Update cargos for rec and dnsdist

42241 of 92606 branches covered (45.61%)

Branch coverage included in aggregate %.

4 of 4 new or added lines in 1 file covered. (100.0%)

69 existing lines in 12 files now uncovered.

128485 of 166001 relevant lines covered (77.4%)

5145339.24 hits per line

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

70.13
/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,344✔
30
  // cerr<<"result = "<<result<<endl;
31
  if (result == SQL_SUCCESS || result == SQL_SUCCESS_WITH_INFO)
1,710,344!
32
    return true;
1,710,344✔
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,874✔
70
    d_query = query;
58,874✔
71
    d_conn = connection;
58,874✔
72
    d_dolog = dolog;
58,874✔
73
    d_residx = 0;
58,874✔
74
    d_paridx = 0;
58,874✔
75
    d_result = SQL_NO_DATA;
58,874✔
76
    d_statement = NULL;
58,874✔
77
    d_prepared = false;
58,874✔
78
    m_columncount = 0;
58,874✔
79
    d_parnum = nparams;
58,874✔
80
  }
58,874✔
81

82
  struct ODBCParam
83
  {
84
    SQLPOINTER ParameterValuePtr;
85
    SQLLEN* LenPtr;
86
    SQLSMALLINT ParameterType;
87
    SQLSMALLINT ValueType;
88
    size_t ParameterAllocSize; // size allocated for ParameterValuePtr, if ParameterType == SQL_INTEGER
89
  };
90

91
  vector<ODBCParam> d_req_bind;
92

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

113
    return this;
265,497✔
114
  }
265,497✔
115

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

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

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

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

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

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

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

175
  SSqlStatement* bind(const string& name, const std::string& value) override
176
  {
133,133✔
177

178
    // cerr<<"asked to bind string "<<value<<endl;
179

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

185
    p.ParameterValuePtr = (char*)new char[value.size() + 1];
133,133✔
186
    value.copy((char*)p.ParameterValuePtr, value.size());
133,133✔
187
    ((char*)p.ParameterValuePtr)[value.size()] = 0;
133,133✔
188
    p.LenPtr = new SQLLEN;
133,133✔
189
    *(p.LenPtr) = value.size();
133,133✔
190
    p.ParameterType = SQL_VARCHAR;
133,133✔
191
    p.ValueType = SQL_C_CHAR;
133,133✔
192

193
    return bind(name, p);
133,133✔
194
  }
133,133✔
195

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

201
    prepareStatement();
276✔
202
    ODBCParam p;
276✔
203

204
    p.ParameterValuePtr = NULL;
276✔
205
    p.LenPtr = new SQLLEN;
276✔
206
    *(p.LenPtr) = SQL_NULL_DATA;
276✔
207
    p.ParameterType = SQL_VARCHAR;
276✔
208
    p.ValueType = SQL_C_CHAR;
276✔
209

210
    return bind(name, p);
276✔
211
  }
276✔
212

213
  SSqlStatement* execute() override
214
  {
71,851✔
215
    prepareStatement();
71,851✔
216
    SQLRETURN result;
71,851✔
217
    // cerr<<"execute("<<d_query<<")"<<endl;
218
    if (d_dolog) {
71,851!
219
      g_log << Logger::Warning << "Query: " << d_query << endl;
×
220
    }
×
221

222
    result = SQLExecute(d_statement);
71,851✔
223
    if (result != SQL_NO_DATA) // odbc+sqlite returns this on 'no rows updated'
71,851✔
224
      testResult(result, SQL_HANDLE_STMT, d_statement, "Could not execute query (" + d_query + ").");
71,839✔
225

226
    // Determine the number of columns.
227
    result = SQLNumResultCols(d_statement, &m_columncount);
71,851✔
228
    testResult(result, SQL_HANDLE_STMT, d_statement, "Could not determine the number of columns.");
71,851✔
229
    // cerr<<"got "<<m_columncount<<" columns"<<endl;
230

231
    if (m_columncount) {
71,851✔
232
      // cerr<<"first SQLFetch"<<endl;
233
      d_result = SQLFetch(d_statement);
9,979✔
234
      // cerr<<"first SQLFetch done, d_result="<<d_result<<endl;
235
    }
9,979✔
236
    else
61,872✔
237
      d_result = SQL_NO_DATA;
61,872✔
238

239
    if (d_result != SQL_NO_DATA)
71,851✔
240
      testResult(d_result, SQL_HANDLE_STMT, d_statement, "Could not do first SQLFetch for (" + d_query + ").");
8,774✔
241
    return this;
71,851✔
242
  }
71,851✔
243

244
  bool hasNextRow() override
245
  {
143,601✔
246
    // cerr<<"hasNextRow d_result="<<d_result<<endl;
247
    return d_result != SQL_NO_DATA;
143,601✔
248
  }
143,601✔
249
  SSqlStatement* nextRow(row_t& row) override;
250

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

263
  SSqlStatement* reset() override
264
  {
125,854✔
265
    SQLCloseCursor(d_statement); // hack, this probably violates some state transitions
125,854✔
266

267
    for (auto& i : d_req_bind) {
269,152✔
268
      // NOLINTBEGIN(cppcoreguidelines-owning-memory)
269
      if (i.ParameterType == SQL_VARCHAR) {
265,497✔
270
        delete[] static_cast<char*>(i.ParameterValuePtr);
133,409✔
271
      }
133,409✔
272
      else if (i.ParameterType == SQL_INTEGER) {
132,088!
273
        if (i.ParameterAllocSize == sizeof(UDWORD)) {
132,088!
274
          delete static_cast<UDWORD*>(i.ParameterValuePtr);
132,088✔
275
        }
132,088✔
276
        else {
×
277
          delete static_cast<ULONG*>(i.ParameterValuePtr);
×
278
        }
×
279
      }
132,088✔
280
      else if (i.ParameterType == SQL_C_UBIGINT) {
×
281
        delete static_cast<unsigned long long*>(i.ParameterValuePtr);
×
282
      }
×
283
      // NOLINTEND(cppcoreguidelines-owning-memory)
284

285
      delete i.LenPtr;
265,497✔
286
    }
265,497✔
287
    d_req_bind.clear();
125,854✔
288
    d_residx = 0;
125,854✔
289
    d_paridx = 0;
125,854✔
290
    return this;
125,854✔
291
  }
125,854✔
292
  const std::string& getQuery() override { return d_query; }
×
293

294
  ~SODBCStatement() override
295
  {
53,951✔
296
    releaseStatement();
53,951✔
297
  }
53,951✔
298

299
private:
300
  void testResult(SQLRETURN result, SQLSMALLINT type, SQLHANDLE handle, const std::string& message)
301
  {
1,706,172✔
302
    std::string errorMessage;
1,706,172✔
303
    if (!realTestResult(result, type, handle, message, errorMessage)) {
1,706,172!
304
      releaseStatement();
×
305
      throw SSqlException(errorMessage);
×
306
    }
×
307
  }
1,706,172✔
308

309
  void releaseStatement()
310
  {
53,947✔
311
    reset();
53,947✔
312
    if (d_statement != NULL)
53,947✔
313
      SQLFreeHandle(SQL_HANDLE_STMT, d_statement);
1,978✔
314
    d_prepared = false;
53,947✔
315
  }
53,947✔
316

317
  void prepareStatement()
318
  {
734,795✔
319
    if (d_prepared)
734,795✔
320
      return;
732,606✔
321

322
    SQLRETURN result;
2,189✔
323

324
    // Allocate statement handle.
325
    result = SQLAllocHandle(SQL_HANDLE_STMT, d_conn, &d_statement);
2,189✔
326
    testResult(result, SQL_HANDLE_DBC, d_conn, "Could not allocate a statement handle.");
2,189✔
327

328
    result = SQLPrepare(d_statement, (SQLCHAR*)d_query.c_str(), SQL_NTS);
2,189✔
329
    testResult(result, SQL_HANDLE_STMT, d_statement, "Could not prepare query.");
2,189✔
330

331
    SQLSMALLINT paramcount;
2,189✔
332
    result = SQLNumParams(d_statement, &paramcount);
2,189✔
333
    testResult(result, SQL_HANDLE_STMT, d_statement, "Could not get parameter count.");
2,189✔
334

335
    if (paramcount != static_cast<SQLSMALLINT>(d_parnum)) {
2,189!
336
      releaseStatement();
×
337
      throw SSqlException("Provided parameter count does not match statement: " + d_query);
×
338
    }
×
339

340
    // cerr<<"prepared ("<<query<<")"<<endl;
341
    d_prepared = true;
2,189✔
342
  }
2,189✔
343

344
  string d_query;
345
  bool d_dolog;
346
  bool d_prepared;
347
  int d_residx;
348
  size_t d_paridx, d_parnum;
349
  SQLRETURN d_result;
350

351
  SQLHDBC d_conn;
352
  SQLHSTMT d_statement; //!< Database statement handle.
353

354
  //! Column type.
355
  struct column_t
356
  {
357
    SQLSMALLINT m_type; //!< Type of the column.
358
    SQLULEN m_size; //!< Column size.
359
    SQLPOINTER m_pData; //!< Pointer to the memory where to store the data.
360
    bool m_canBeNull; //!< Can this column be null?
361
  };
362

363
  //! Column info.
364
  SQLSMALLINT m_columncount;
365
};
366

367
SSqlStatement* SODBCStatement::nextRow(row_t& row)
368
{
133,622✔
369
  SQLRETURN result;
133,622✔
370

371
  row.clear();
133,622✔
372

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

390
    // Done!
391
    d_residx++;
133,622✔
392
    // cerr<<"SQLFetch"<<endl;
393
    d_result = SQLFetch(d_statement);
133,622✔
394
    // cerr<<"subsequent SQLFetch done, d_result="<<d_result<<endl;
395
    if (d_result == SQL_NO_DATA) {
133,622✔
396
      SQLRETURN result2 = SQLMoreResults(d_statement);
8,774✔
397
      // cerr<<"SQLMoreResults done, result="<<d_result2<<endl;
398
      if (result2 == SQL_NO_DATA) {
8,774!
399
        d_result = result2;
8,774✔
400
      }
8,774✔
UNCOV
401
      else {
×
UNCOV
402
        testResult(result2, SQL_HANDLE_STMT, d_statement, "Could not fetch next result set for (" + d_query + ").");
×
UNCOV
403
        d_result = SQLFetch(d_statement);
×
UNCOV
404
      }
×
405
    }
8,774✔
406
    testResult(result, SQL_HANDLE_STMT, d_statement, "Could not do subsequent SQLFetch for (" + d_query + ").");
133,622✔
407

408
    return this;
133,622✔
409
  }
133,622✔
410

UNCOV
411
  SQLFreeStmt(d_statement, SQL_CLOSE);
×
UNCOV
412
  throw SSqlException("Should not get here.");
×
413
}
133,622✔
414

415
// Constructor.
416
SODBC::SODBC(
417
  const std::string& dsn,
418
  const std::string& username,
419
  const std::string& password)
420
{
865✔
421
  SQLRETURN result;
865✔
422

423
  // Allocate an environment handle.
424
  result = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &m_environment);
865✔
425
  testResult(result, SQL_NULL_HANDLE, NULL, "Could not allocate an environment handle.");
865✔
426

427
  // Set ODBC version. (IEUW!)
428
  result = SQLSetEnvAttr(m_environment, SQL_ATTR_ODBC_VERSION, reinterpret_cast<void*>(SQL_OV_ODBC3), 0);
865✔
429
  testResult(result, SQL_HANDLE_ENV, m_environment, "Could not set the ODBC version.");
865✔
430

431
  // Allocate connection handle.
432
  result = SQLAllocHandle(SQL_HANDLE_DBC, m_environment, &m_connection);
865✔
433
  testResult(result, SQL_HANDLE_ENV, m_environment, "Could not allocate a connection handle.");
865✔
434

435
  // Connect to the database.
436
  char* l_dsn = strdup(dsn.c_str());
865✔
437
  char* l_username = strdup(username.c_str());
865✔
438
  char* l_password = strdup(password.c_str());
865✔
439

440
  result = SQLConnect(m_connection,
865✔
441
                      reinterpret_cast<SQLTCHAR*>(l_dsn), dsn.length(),
865✔
442
                      reinterpret_cast<SQLTCHAR*>(l_username), username.length(),
865✔
443
                      reinterpret_cast<SQLTCHAR*>(l_password), password.length());
865✔
444

445
  free(l_dsn);
865✔
446
  free(l_username);
865✔
447
  free(l_password);
865✔
448

449
  testResult(result, SQL_HANDLE_DBC, m_connection, "Could not connect to ODBC datasource.");
865✔
450

451
  m_busy = false;
865✔
452
  m_log = false;
865✔
453
}
865✔
454

455
// Destructor.
456
SODBC::~SODBC()
457
{
803✔
458
  // Disconnect from database and free all used resources.
459
  // SQLFreeHandle( SQL_HANDLE_STMT, m_statement );
460

461
  SQLDisconnect(m_connection);
803✔
462

463
  SQLFreeHandle(SQL_HANDLE_DBC, m_connection);
803✔
464
  SQLFreeHandle(SQL_HANDLE_ENV, m_environment);
803✔
465

466
  // Free all allocated column memory.
467
  // for ( int i = 0; i < m_columnInfo.size(); i++ )
468
  // {
469
  //   if ( m_columnInfo[ i ].m_pData )
470
  //     delete m_columnInfo[ i ].m_pData;
471
  // }
472
}
803✔
473

474
// Executes a command.
475
void SODBC::execute(const std::string& command)
476
{
×
477
  SODBCStatement stmt(command, m_log, 0, m_connection);
×
478

479
  stmt.execute()->reset();
×
480
}
×
481

482
// Sets the log state.
483
void SODBC::setLog(bool state)
484
{
866✔
485
  m_log = state;
866✔
486
}
866✔
487

488
// Returns an exception.
489
SSqlException SODBC::sPerrorException(const std::string& reason)
490
{
×
491
  return SSqlException(reason);
×
492
}
×
493

494
std::unique_ptr<SSqlStatement> SODBC::prepare(const string& query, int nparams)
495
{
58,886✔
496
  return std::make_unique<SODBCStatement>(query, m_log, nparams, m_connection);
58,886✔
497
}
58,886✔
498

499
void SODBC::startTransaction()
500
{
244✔
501
  // cerr<<"starting transaction"<<endl;
502
  SQLRETURN result;
244✔
503
  result = SQLSetConnectAttr(m_connection, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
244✔
504
  testResult(result, SQL_HANDLE_DBC, m_connection, "startTransaction (enable autocommit) failed");
244✔
505
}
244✔
506

507
void SODBC::commit()
508
{
232✔
509
  // cerr<<"commit!"<<endl;
510
  SQLRETURN result;
232✔
511

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

515
  result = SQLSetConnectAttr(m_connection, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
232✔
516
  testResult(result, SQL_HANDLE_DBC, m_connection, "disabling autocommit after commit failed");
232✔
517
}
232✔
518

519
void SODBC::rollback()
520
{
×
521
  // cerr<<"rollback!"<<endl;
522
  SQLRETURN result;
×
523

524
  result = SQLEndTran(SQL_HANDLE_DBC, m_connection, SQL_ROLLBACK);
×
525
  testResult(result, SQL_HANDLE_DBC, m_connection, "rollback failed");
×
526

527
  result = SQLSetConnectAttr(m_connection, SQL_ATTR_AUTOCOMMIT, SQL_AUTOCOMMIT_OFF, 0);
×
528
  testResult(result, SQL_HANDLE_DBC, m_connection, "disabling autocommit after rollback failed");
×
529
}
×
530

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

© 2026 Coveralls, Inc