]>
Commit | Line | Data |
---|---|---|
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 | 28 | static 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 | 65 | class SODBCStatement : public SSqlStatement |
4f983d1b PD |
66 | { |
67 | public: | |
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 |
285 | private: |
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, ¶mcount); | |
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 | ||
353 | SSqlStatement* 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. | |
402 | SODBC::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 | 442 | SODBC::~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 | 461 | void 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 | 469 | void SODBC::setLog(bool state) |
4f983d1b PD |
470 | { |
471 | m_log = state; | |
472 | } | |
473 | ||
474 | // Returns an exception. | |
ff05c7e1 | 475 | SSqlException SODBC::sPerrorException(const std::string& reason) |
4f983d1b | 476 | { |
ff05c7e1 | 477 | return SSqlException(reason); |
4f983d1b PD |
478 | } |
479 | ||
a59a9c23 | 480 | std::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 |
485 | void 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 |
493 | void 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 |
505 | void 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 |
517 | void 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 | } |