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

PowerDNS / pdns / 17795654203

17 Sep 2025 11:10AM UTC coverage: 66.037% (+0.03%) from 66.012%
17795654203

Pull #15808

github

web-flow
Merge 103733592 into a7f305b95
Pull Request #15808: rec: allow overriding remote address from preoutquery()

42442 of 92980 branches covered (45.65%)

Branch coverage included in aggregate %.

7 of 7 new or added lines in 2 files covered. (100.0%)

18 existing lines in 6 files now uncovered.

128910 of 166497 relevant lines covered (77.42%)

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

113
    return this;
265,478✔
114
  }
265,478✔
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,285✔
130
    prepareStatement();
70,285✔
131
    return bind(name, (uint32_t)value);
70,285✔
132
  }
70,285✔
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,076✔
142
    prepareStatement();
132,076✔
143
    ODBCParam p;
132,076✔
144
    p.ParameterValuePtr = new UDWORD{value};
132,076✔
145
    p.LenPtr = new SQLLEN{sizeof(UDWORD)};
132,076✔
146
    p.ParameterType = SQL_INTEGER;
132,076✔
147
    p.ValueType = SQL_INTEGER;
132,076✔
148
    p.ParameterAllocSize = sizeof(UDWORD);
132,076✔
149
    return bind(name, p);
132,076✔
150
  }
132,076✔
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,126✔
177

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

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

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

193
    return bind(name, p);
133,126✔
194
  }
133,126✔
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,840✔
215
    prepareStatement();
71,840✔
216
    SQLRETURN result;
71,840✔
217
    // cerr<<"execute("<<d_query<<")"<<endl;
218
    if (d_dolog) {
71,840!
219
      g_log << Logger::Warning << "Query: " << d_query << endl;
×
220
    }
×
221

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

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

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

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

244
  bool hasNextRow() override
245
  {
143,556✔
246
    // cerr<<"hasNextRow d_result="<<d_result<<endl;
247
    return d_result != SQL_NO_DATA;
143,556✔
248
  }
143,556✔
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,862✔
265
    SQLCloseCursor(d_statement); // hack, this probably violates some state transitions
125,862✔
266

267
    for (auto& i : d_req_bind) {
269,133✔
268
      // NOLINTBEGIN(cppcoreguidelines-owning-memory)
269
      if (i.ParameterType == SQL_VARCHAR) {
265,478✔
270
        delete[] static_cast<char*>(i.ParameterValuePtr);
133,402✔
271
      }
133,402✔
272
      else if (i.ParameterType == SQL_INTEGER) {
132,076!
273
        if (i.ParameterAllocSize == sizeof(UDWORD)) {
132,076!
274
          delete static_cast<UDWORD*>(i.ParameterValuePtr);
132,076✔
275
        }
132,076✔
276
        else {
×
277
          delete static_cast<ULONG*>(i.ParameterValuePtr);
×
278
        }
×
279
      }
132,076✔
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,478✔
286
    }
265,478✔
287
    d_req_bind.clear();
125,862✔
288
    d_residx = 0;
125,862✔
289
    d_paridx = 0;
125,862✔
290
    return this;
125,862✔
291
  }
125,862✔
292
  const std::string& getQuery() override { return d_query; }
×
293

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

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

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

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

322
    SQLRETURN result;
2,193✔
323

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

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

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

335
    if (paramcount != static_cast<SQLSMALLINT>(d_parnum)) {
2,193!
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,193✔
342
  }
2,193✔
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,588✔
369
  SQLRETURN result;
133,588✔
370

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

373
  result = d_result;
133,588✔
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,588!
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,467✔
380
      SQLLEN len;
1,147,879✔
381
      SQLCHAR coldata[128 * 1024];
1,147,879✔
382
      std::string strres = "";
1,147,879✔
383
      result = SQLGetData(d_statement, i + 1, SQL_C_CHAR, (SQLPOINTER)coldata, sizeof(coldata), &len);
1,147,879✔
384
      testResult(result, SQL_HANDLE_STMT, d_statement, "Could not get data.");
1,147,879✔
385
      if (len > SQL_NULL_DATA)
1,147,879✔
386
        strres = std::string(reinterpret_cast<const char*>(coldata), std::min<SQLLEN>(sizeof(coldata) - 1, len)); // do not use nil byte
1,021,407✔
387
      row.push_back(strres);
1,147,879✔
388
    }
1,147,879✔
389

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

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

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

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

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

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

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

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

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

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

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

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

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

461
  SQLDisconnect(m_connection);
802✔
462

463
  SQLFreeHandle(SQL_HANDLE_DBC, m_connection);
802✔
464
  SQLFreeHandle(SQL_HANDLE_ENV, m_environment);
802✔
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
}
802✔
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,885✔
496
  return std::make_unique<SODBCStatement>(query, m_log, nparams, m_connection);
58,885✔
497
}
58,885✔
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

© 2025 Coveralls, Inc