25 using namespace libjmmcg;
26 using namespace NTUtils::Database::ODBC;
30 const SQLUINTEGER rs_count_timeout=60;
31 const TCHAR timeout_sql_state[]=_T(
"HYT0");
35 const SQLTCHAR *
const SqlState;
36 const SQLTCHAR *
const Msg;
37 const SQLINTEGER NativeError;
39 inline ODBCErrInfo(
const SQLTCHAR *
const a,
const SQLTCHAR *
const b,
const SQLINTEGER c) : SqlState(a), Msg(b), NativeError(c) {};
40 inline ~ODBCErrInfo(
void) {};
41 friend inline tostream &
__fastcall operator<<(tostream &o,
const ODBCErrInfo i) {o<<_T(
"State: '")<<
reinterpret_cast<
const TCHAR *
const>(i.SqlState)<<_T(
"'\nMessage: '")<<
reinterpret_cast<
const TCHAR *
const>(i.Msg)<<_T(
"'\nNative error number: ")<<i.NativeError<<std::endl;
return o;};
44 inline ODBCErrInfo(
void) : SqlState(NULL), Msg(NULL), NativeError(NULL) {};
50 Database::ODBC::Exceptions::ODBCExceptionErr::ODBCExceptionErr(
const SQLHANDLE *
const hstmt,
const SQLSMALLINT type,
const SQLRETURN err) : std::exception() {
51 m_info<<_T(
"ODBC error details:\n");
53 m_info<<GetODBCERRInfoTimeout(hstmt, type, timed_out)<<std::endl;
55 m_info<<_T(
"Timed out.\n");
57 m_info<<_T(
"ODBC error raw code:")<<err<<std::endl;
61 Database::ODBC::Exceptions::ODBCExceptionErr::~ODBCExceptionErr(
void) {
62 m_info<<_T(
"ODBC Wrapper error.\n");
66 const TCHAR *
__fastcall Database::ODBC::Exceptions::ODBCExceptionErr::what(
void) {
67 tstring str(m_info.str());
68 str_hack=std::auto_ptr<TCHAR>(
new TCHAR[str.size()+
sizeof(TCHAR)]);
69 memcpy(str_hack.get(), str.c_str(),
sizeof(TCHAR)*str.size());
70 return str_hack.get();
74 tstring
__fastcall Database::ODBC::Exceptions::ODBCExceptionErr::GetODBCERRInfoTimeout(
const SQLHANDLE *
const hstmt,
const SQLSMALLINT type,
bool &timed_out) {
75 SQLTCHAR SqlState[6], Msg[SQL_MAX_MESSAGE_LENGTH];
76 SQLINTEGER NativeError;
77 SQLSMALLINT i=1, MsgLen;
81 while ((rc2=SQLGetDiagRec(type, *hstmt, i, SqlState, &NativeError, Msg,
sizeof(Msg), &MsgLen)) != SQL_NO_DATA) {
82 const tstring state(
reinterpret_cast<TCHAR *>(SqlState));
83 if (state.find(timeout_sql_state)!=tstring::npos) {
86 ss<<ODBCErrInfo(SqlState, Msg, NativeError);
93 Database::ODBC::Exceptions::EnvironmentErr::EnvironmentErr(
const tstring &str,
const SQLHANDLE *
const hstmt,
const SQLSMALLINT type,
const SQLRETURN err) : ODBCExceptionErr(hstmt, type, err) {
94 m_info<<_T(
"Environment error. Details:\n");
95 m_info<<str<<std::endl;
99 Database::ODBC::Exceptions::EnvironmentErr::~EnvironmentErr(
void) {
103 Database::ODBC::Exceptions::ConnectionErr::ConnectionErr(
const tstring &str,
const SQLHANDLE *
const hstmt,
const SQLSMALLINT type,
const SQLRETURN err) : ODBCExceptionErr(hstmt, type, err) {
104 m_info<<_T(
"Connection error. Details:\n");
105 m_info<<str<<std::endl;
109 Database::ODBC::Exceptions::ConnectionErr::ConnectionErr(
const tstring &str,
const tstring &sql,
const SQLHANDLE *
const hstmt,
const SQLSMALLINT type,
const SQLRETURN err) : ODBCExceptionErr(hstmt, type, err) {
110 m_info<<_T(
"Connection error. Details:\n");
111 m_info<<str<<std::endl;
112 m_info<<_T(
"SQL statement:\n");
113 m_info<<sql<<std::endl;
117 Database::ODBC::Exceptions::ConnectionErr::~ConnectionErr(
void) {
121 Database::ODBC::Exceptions::RecordsetBaseErr::RecordsetBaseErr(
const tstring &str,
const SQLHANDLE *
const hstmt,
const SQLSMALLINT type,
const SQLRETURN err) : ODBCExceptionErr(hstmt, type, err) {
122 m_info<<_T(
"Record set error. Details:\n");
123 m_info<<str<<std::endl;
127 Database::ODBC::Exceptions::RecordsetBaseErr::RecordsetBaseErr(
const tstring &str,
const tstring &sql,
const SQLHANDLE *
const hstmt,
const SQLSMALLINT type,
const SQLRETURN err) : ODBCExceptionErr(hstmt, type, err) {
128 m_info<<_T(
"Record set error. Details:\n");
129 m_info<<str<<std::endl;
130 m_info<<_T(
"SQL statement:\n");
131 m_info<<sql<<std::endl;
135 Database::ODBC::Exceptions::RecordsetBaseErr::~RecordsetBaseErr(
void) {
136 m_info<<_T(
"Read-only record set error.\n");
140 Database::ODBC::Exceptions::RORecordsetBaseErr::RORecordsetBaseErr(
const tstring &str,
const SQLHANDLE *
const hstmt,
const SQLSMALLINT type,
const SQLRETURN err) : RecordsetBaseErr(str, hstmt, type, err) {
141 m_info<<_T(
"Read-only record set error.\n");
145 Database::ODBC::Exceptions::RORecordsetBaseErr::RORecordsetBaseErr(
const tstring &str,
const tstring &sql,
const SQLHANDLE *
const hstmt,
const SQLSMALLINT type,
const SQLRETURN err) : RecordsetBaseErr(str, sql, hstmt, type, err) {
149 Database::ODBC::Exceptions::RORecordsetBaseErr::RORecordsetBaseErr(
const SQLUSMALLINT col_num,
const type_info &data,
const tstring &str,
const SQLHANDLE *
const hstmt,
const SQLSMALLINT type,
const SQLRETURN err) : RecordsetBaseErr(str, hstmt, type, err) {
150 m_info<<_T(
"Read-only record set error. Parameters:\n");
151 m_info<<_T(
"Column number: ")<<col_num<<std::endl;
152 m_info<<_T(
"Parameter type: '")<<data.name()<<_T(
"'")<<std::endl;
156 Database::ODBC::Exceptions::RORecordsetBaseErr::RORecordsetBaseErr(
const SQLUSMALLINT col_num,
const type_info &data,
const tstring &str,
const tstring &sql,
const SQLHANDLE *
const hstmt,
const SQLSMALLINT type,
const SQLRETURN err) : RecordsetBaseErr(str, sql, hstmt, type, err) {
157 m_info<<_T(
"Read-only record set error. Parameters:\n");
158 m_info<<_T(
"Column number: ")<<col_num<<std::endl;
159 m_info<<_T(
"Parameter type: '")<<data.name()<<_T(
"'")<<std::endl;
163 Database::ODBC::Exceptions::RORecordsetBaseErr::~RORecordsetBaseErr(
void) {
164 m_info<<_T(
"Write-only connection error.\n");
168 Database::ODBC::Exceptions::WOConnectionBaseErr::WOConnectionBaseErr(
const tstring &str,
const SQLHANDLE *
const hstmt,
const SQLSMALLINT type,
const SQLRETURN err) : RORecordsetBaseErr(str, hstmt, type, err) {
169 m_info<<_T(
"Write-only connection error.\n");
173 Database::ODBC::Exceptions::WOConnectionBaseErr::WOConnectionBaseErr(
const tstring &str,
const tstring &sql,
const SQLHANDLE *
const hstmt,
const SQLSMALLINT type,
const SQLRETURN err) : RORecordsetBaseErr(str, sql, hstmt, type, err) {
174 m_info<<_T(
"Write-only connection error.\n");
178 Database::ODBC::Exceptions::WOConnectionBaseErr::WOConnectionBaseErr(
const SQLUSMALLINT col_num,
const type_info &data,
const tstring &str,
const SQLHANDLE *
const hstmt,
const SQLSMALLINT type,
const SQLRETURN err) : RORecordsetBaseErr(col_num, data, str, hstmt, type, err) {
179 m_info<<_T(
"Write-only connection error.\n");
183 Database::ODBC::Exceptions::WOConnectionBaseErr::WOConnectionBaseErr(
const SQLUSMALLINT col_num,
const type_info &data,
const tstring &str,
const tstring &sql,
const SQLHANDLE *
const hstmt,
const SQLSMALLINT type,
const SQLRETURN err) : RORecordsetBaseErr(col_num, data, str, sql, hstmt, type, err) {
184 m_info<<_T(
"Write-only connection error.\n");
188 Database::ODBC::Exceptions::WOConnectionBaseErr::~WOConnectionBaseErr(
void) {
192 Environment::Environment(
const bool read_only,
const SQLUINTEGER timeout) : henv(NULL), hdbc(NULL) {
193 SQLRETURN ret=SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
194 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
195 throw Database::ODBC::Exceptions::EnvironmentErr(_T(
"Failed to allocate ODBC handle."), &henv, SQL_HANDLE_ENV, ret);
197 ret=SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION,
reinterpret_cast<SQLPOINTER>(SQL_OV_ODBC3), 0);
198 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
199 throw Database::ODBC::Exceptions::EnvironmentErr(_T(
"Failed to set ODBC version behaviour as v3.x on the environment handle."), &henv, SQL_HANDLE_ENV, ret);
201 ret=SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
202 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
203 throw Database::ODBC::Exceptions::EnvironmentErr(_T(
"Failed to allocate data base connection handle."), &henv, SQL_HANDLE_ENV, ret);
205 ret=SQLSetConnectAttr(hdbc, SQL_LOGIN_TIMEOUT,
reinterpret_cast<SQLPOINTER>(timeout), 0);
206 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
207 throw Database::ODBC::Exceptions::EnvironmentErr(_T(
"Failed to set the login timeout on the data base connection handle."), &hdbc, SQL_HANDLE_DBC, ret);
209 ret=SQLSetConnectAttr(hdbc, SQL_ATTR_AUTOCOMMIT,
reinterpret_cast<SQLPOINTER>(read_only ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF), 0);
210 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
211 throw Database::ODBC::Exceptions::EnvironmentErr(_T(
"Failed to set the auto commit feature on the data base connection handle."), &hdbc, SQL_HANDLE_DBC, ret);
213 ret=SQLSetConnectAttr(hdbc, SQL_ATTR_CONNECTION_POOLING,
reinterpret_cast<SQLPOINTER>(SQL_CP_ONE_PER_HENV), 0);
214 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
215 throw Database::ODBC::Exceptions::EnvironmentErr(_T(
"Failed to enable connection pooling on the data base connection handle."), &hdbc, SQL_HANDLE_DBC, ret);
217 ret=SQLSetConnectAttr(hdbc, SQL_ATTR_CP_MATCH,
reinterpret_cast<SQLPOINTER>(SQL_CP_STRICT_MATCH), 0);
218 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
219 throw Database::ODBC::Exceptions::EnvironmentErr(_T(
"Failed to set the connection pooling matching algorithm on the data base connection handle."), &hdbc, SQL_HANDLE_DBC, ret);
221 ret=SQLSetConnectAttr(hdbc, SQL_ATTR_ACCESS_MODE,
reinterpret_cast<SQLPOINTER>(read_only ? SQL_MODE_READ_ONLY : SQL_MODE_READ_WRITE), 0);
222 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
223 throw Database::ODBC::Exceptions::EnvironmentErr(_T(
"Failed to set the read/write attributes on the data base connection handle."), &hdbc, SQL_HANDLE_DBC, ret);
225 ret=SQLSetConnectAttr(hdbc, SQL_ATTR_TXN_ISOLATION,
reinterpret_cast<SQLPOINTER>(read_only ? SQL_TXN_READ_UNCOMMITTED : SQL_TXN_SERIALIZABLE), 0);
226 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
227 throw Database::ODBC::Exceptions::EnvironmentErr(_T(
"Failed to set the transation isolation level on the data base connection handle."), &hdbc, SQL_HANDLE_DBC, ret);
232 Environment::~Environment(
void) {
234 SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
237 SQLFreeHandle(SQL_HANDLE_ENV, henv);
243 Connection::Connection(
const tstring &cnx,
const bool read_only,
const SQLUINTEGER timeout) : Environment(read_only, timeout), stmt_timeout(timeout), hstmt(NULL) {
244 const SQLSMALLINT cnx_buff_sz=1024;
245 SQLTCHAR cnx_buff[cnx_buff_sz];
246 SQLSMALLINT StringLength2Ptr;
247 const SQLRETURN ret=SQLDriverConnect(hdbc, NULL,
const_cast<SQLTCHAR *>(cnx.c_str()), SQL_NTS, cnx_buff, cnx_buff_sz, &StringLength2Ptr, SQL_DRIVER_COMPLETE);
249 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
250 throw Database::ODBC::Exceptions::ConnectionErr(_T(
"Failed to connect to the supplied DSN."), &hdbc, SQL_HANDLE_DBC, ret);
256 Connection::~Connection(
void) {
258 SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
264 void __fastcall Connection::execute(
const tstring &sql) {
265 const SQLRETURN ret=SQLExecDirect(hstmt,
const_cast<SQLTCHAR *>(sql.c_str()), SQL_NTS);
266 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
267 throw Database::ODBC::Exceptions::RecordsetBaseErr(_T(
"Failed to execute the SQL statement on the statement handle."), sql, &hstmt, SQL_HANDLE_STMT, ret);
272 void __fastcall Connection::AllocateStmt(
void) {
273 SQLRETURN ret=SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
274 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
275 throw Database::ODBC::Exceptions::ConnectionErr(_T(
"Failed to allocate the SQL statement handle on the data base handle."), &hdbc, SQL_HANDLE_DBC, ret);
277 ret=SQLSetStmtAttr(hstmt, SQL_ATTR_CONCURRENCY,
reinterpret_cast<SQLPOINTER>(SQL_CONCUR_READ_ONLY), 0);
278 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
279 throw Database::ODBC::Exceptions::ConnectionErr(_T(
"Failed to set the concurrency attribute as a forward-only cursor on the statement handle."), &hstmt, SQL_HANDLE_STMT, ret);
281 ret=SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_TYPE,
reinterpret_cast<SQLPOINTER>(SQL_CURSOR_FORWARD_ONLY), 0);
282 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
283 throw Database::ODBC::Exceptions::ConnectionErr(_T(
"Failed to set the cursor attribute as a forward-only cursor on the statement handle."), &hstmt, SQL_HANDLE_STMT, ret);
285 ret=SQLSetStmtAttr(hstmt, SQL_ATTR_CURSOR_SCROLLABLE,
reinterpret_cast<SQLPOINTER>(SQL_NONSCROLLABLE), 0);
286 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
287 throw Database::ODBC::Exceptions::ConnectionErr(_T(
"Failed to set the non-scollable cursor attribute on the statement handle."), &hstmt, SQL_HANDLE_STMT, ret);
289 ret=SQLSetStmtAttr(hstmt, SQL_ATTR_QUERY_TIMEOUT,
reinterpret_cast<SQLPOINTER>(stmt_timeout), 0);
290 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
291 throw Database::ODBC::Exceptions::ConnectionErr(_T(
"Failed to set the query time-out attribute on the statement handle."), &hstmt, SQL_HANDLE_STMT, ret);
293 ret=SQLSetStmtAttr(hstmt, SQL_ATTR_RETRIEVE_DATA,
reinterpret_cast<SQLPOINTER>(SQL_RD_ON), 0);
294 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
295 throw Database::ODBC::Exceptions::ConnectionErr(_T(
"Failed to set the data retrieval attribute on the statement handle."), &hstmt, SQL_HANDLE_STMT, ret);
297 ret=SQLSetStmtAttr(hstmt, SQL_ATTR_NOSCAN,
reinterpret_cast<SQLPOINTER>(SQL_NOSCAN_ON), 0);
298 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
299 throw Database::ODBC::Exceptions::ConnectionErr(_T(
"Failed to turn off DBMS-specific scanning on the statement handle."), &hstmt, SQL_HANDLE_STMT, ret);
304 RecordsetBase::RecordsetBase(
const tstring &cnx,
const bool read_only,
const SQLUINTEGER timeout) : Connection(cnx, read_only, timeout), allocated_cursor(
false), row_count(-1) {
308 RecordsetBase::~RecordsetBase(
void) {
309 if (allocated_cursor) {
310 SQLCloseCursor(hstmt);
315 void __fastcall RecordsetBase::execute(
const tstring &sql) {
316 Connection::execute(sql);
317 allocated_cursor=
true;
322 void __fastcall RecordsetBase::RowsAffected(
void) {
323 const SQLRETURN ret=SQLRowCount(hstmt, &row_count);
324 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
325 throw Database::ODBC::Exceptions::RecordsetBaseErr(_T(
"Failed to get the row count from the statement handle."), &hstmt, SQL_HANDLE_STMT, ret);
330 RORecordsetBase::RORecordsetBase(
const tstring &cnx,
const SQLUINTEGER timeout) : RecordsetBase(cnx,
true, timeout), no_more_data(
true) {
334 RORecordsetBase::~RORecordsetBase(
void) {
338 void __fastcall RORecordsetBase::execute(
const tstring &sql) {
339 RecordsetBase::execute(sql);
341 const SQLRETURN ret=SQLNumResultCols(hstmt, &num_cols);
343 if (!num_cols || (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO)) {
344 throw Database::ODBC::Exceptions::RORecordsetBaseErr(_T(
"Failed to get the number of columns returned by the SQL statement from the statement handle."), sql, &hstmt, SQL_HANDLE_STMT, ret);
346 column_names.clear();
350 SQLINTEGER
__fastcall RORecordsetBase::count(
void) {
353 RORecordset rs(cnx_string(), rs_count_timeout);
356 rs.execute(_T(
"select count(*) as pants from (") + sql_statement + _T(
") as tmp_cnt_table"));
357 rs.item(1, row_count);
359 return RecordsetBase::count();
364 SQLSMALLINT
__fastcall RORecordsetBase::GetColumnIdx(
const tstring &col_name) {
365 return column_names[col_name];
369 RORecordset::RORecordset(
const tstring &cnx,
const SQLUINTEGER timeout) : RORecordsetBase(cnx, timeout) {
373 RORecordset::~RORecordset(
void) {
377 void __fastcall RORecordset::execute(
const tstring &sql) {
378 RORecordsetBase::execute(sql);
380 SQLTCHAR ColumnName[MAX_PATH];
381 SQLSMALLINT NameLengthPtr;
382 SQLSMALLINT DataTypePtr;
383 SQLUINTEGER ColumnSizePtr;
384 SQLSMALLINT DecimalDigitsPtr;
385 SQLSMALLINT NullablePtr;
387 const SQLRETURN ret=SQLDescribeCol(hstmt, i, ColumnName, MAX_PATH, &NameLengthPtr, &DataTypePtr, &ColumnSizePtr, &DecimalDigitsPtr, &NullablePtr);
388 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
389 throw Database::ODBC::Exceptions::RORecordsetBaseErr(_T(
"Failed to get the required SQL column details from the statement handle."), sql, &hstmt, SQL_HANDLE_STMT, ret);
391 column_names[ColumnName]=i;
392 }
while (++i<=NumColumns());
393 assert(NumColumns()==column_names.size());
399 void __fastcall RORecordset::moveNext(
void) {
400 const SQLRETURN ret=SQLFetch(hstmt);
401 if (ret==SQL_NO_DATA) {
403 }
else if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
404 throw Database::ODBC::Exceptions::RORecordsetBaseErr(_T(
"Failed to fetch the next data on the statement handle."), &hstmt, SQL_HANDLE_STMT, ret);
409 ROBulkRecordset::ROBulkRecordset(
const tstring &cnx,
const SQLUINTEGER row_arr_sz,
const SQLUINTEGER timeout) : RORecordsetBase(cnx, timeout), row_buffer_size(row_arr_sz) {
413 ROBulkRecordset::~ROBulkRecordset(
void) {
417 void __fastcall ROBulkRecordset::execute(
const tstring &sql) {
418 SQLRETURN ret=SQLFreeStmt(hstmt, SQL_UNBIND);
419 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
420 throw Database::ODBC::Exceptions::RORecordsetBaseErr(_T(
"Failed to unbind any old row data blocks from the statement handle."), &hstmt, SQL_HANDLE_STMT, ret);
422 ret=SQLSetStmtAttr(hstmt, SQL_ATTR_RETRIEVE_DATA,
reinterpret_cast<SQLPOINTER>(SQL_RD_OFF), 0);
423 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
424 throw Database::ODBC::Exceptions::RORecordsetBaseErr(_T(
"Failed to set the data retrieval attribute on the statement handle."), &hstmt, SQL_HANDLE_STMT, ret);
426 RORecordsetBase::execute(sql);
429 SQLTCHAR ColumnName[MAX_PATH];
430 SQLSMALLINT NameLengthPtr;
431 SQLSMALLINT DataTypePtr;
432 SQLUINTEGER ColumnSizePtr;
433 SQLSMALLINT DecimalDigitsPtr;
434 SQLSMALLINT NullablePtr;
435 TotalColumnSizePtr=0;
438 ret=SQLDescribeCol(hstmt, i, ColumnName, MAX_PATH, &NameLengthPtr, &DataTypePtr, &ColumnSizePtr, &DecimalDigitsPtr, &NullablePtr);
439 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
441 ss<<_T(
"Failed to get the required ")<<i<<_T(
"-th SQL column details from the statement handle.");
442 throw Database::ODBC::Exceptions::RORecordsetBaseErr(ss.str(), sql, &hstmt, SQL_HANDLE_STMT, ret);
444 column_names[ColumnName]=i;
446 const size_t size=ColumnSizePtr+2*
sizeof(TCHAR);
447 cols_sizes.push_back(std::pair<size_t, size_t>(size, offset));
449 offset+=size+
sizeof(SQLINTEGER);
450 }
while (++i<=NumColumns());
451 TotalColumnSizePtr=offset;
452 assert(NumColumns()==cols_sizes.size());
453 assert(NumColumns()==column_names.size());
454 SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
456 ret=SQLSetStmtAttr(hstmt, SQL_ATTR_RETRIEVE_DATA,
reinterpret_cast<SQLPOINTER>(SQL_RD_ON), 0);
457 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
458 throw Database::ODBC::Exceptions::RORecordsetBaseErr(_T(
"Failed to set the data retrieval attribute on the statement handle."), &hstmt, SQL_HANDLE_STMT, ret);
460 ret=SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_BIND_TYPE,
reinterpret_cast<SQLPOINTER>(TotalColumnSizePtr), 0);
461 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
462 throw Database::ODBC::Exceptions::RORecordsetBaseErr(_T(
"Failed to bind the row data block to the statement handle."), &hstmt, SQL_HANDLE_STMT, ret);
464 ret=SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_ARRAY_SIZE,
reinterpret_cast<SQLPOINTER>(row_buffer_size), 0);
465 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
466 throw Database::ODBC::Exceptions::RORecordsetBaseErr(_T(
"Failed to set the number of rows in the row data block attribute in the statement handle."), &hstmt, SQL_HANDLE_STMT, ret);
469 ret=SQLSetStmtAttr(hstmt, SQL_ATTR_ROW_STATUS_PTR, RowStatusArray.get(), 0);
470 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
471 throw Database::ODBC::Exceptions::RORecordsetBaseErr(_T(
"Failed to bind the rows status array to the statement handle."), &hstmt, SQL_HANDLE_STMT, ret);
473 ret=SQLSetStmtAttr(hstmt, SQL_ATTR_ROWS_FETCHED_PTR, &NumRowsFetched, 0);
474 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
475 throw Database::ODBC::Exceptions::RORecordsetBaseErr(_T(
"Failed to bind the number of rows filled variable to the statement handle."), &hstmt, SQL_HANDLE_STMT, ret);
483 rows_buffer.get()+cols_sizes[i].second,
485 reinterpret_cast<SQLINTEGER *>(
488 i<(cols_sizes.size()-1) ?
489 cols_sizes[i+1].second
496 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
498 ss<<_T(
"Failed to bind the ")<<i<<_T(
"-th element from a row in the row data block to the statement handle.");
499 throw Database::ODBC::Exceptions::RORecordsetBaseErr(ss.str(), &hstmt, SQL_HANDLE_STMT, ret);
501 }
while (++i<cols_sizes.size());
502 RecordsetBase::execute(sql);
509 void __fastcall ROBulkRecordset::moveNext(
void) {
511 if (row_buffer_pos>=NumRowsFetched) {
518 void __fastcall ROBulkRecordset::RawMoveNext(
void) {
519 const SQLRETURN ret=SQLFetch(hstmt);
520 assert(NumRowsFetched>=0);
521 if (ret==SQL_NO_DATA) {
522 assert(NumRowsFetched==0);
525 }
else if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
526 throw Database::ODBC::Exceptions::RORecordsetBaseErr(_T(
"Failed to fetch the next block of data on the statement handle."), &hstmt, SQL_HANDLE_STMT, ret);
528 assert(NumRowsFetched>0);
532 void __fastcall ROBulkRecordset::AllocateDataBlock(
void) {
533 assert(TotalColumnSizePtr);
534 assert(row_buffer_size);
535 rows_buffer=std::auto_ptr<RowBufferElementType>(
new RowBufferElementType[TotalColumnSizePtr*row_buffer_size]);
536 RowStatusArray=std::auto_ptr<SQLUSMALLINT>(
new SQLUSMALLINT[row_buffer_size]);
540 const ROBulkRecordset::RowBufferElementType *
const __fastcall ROBulkRecordset::GetRowElement(
const SQLUSMALLINT col_num)
const {
541 return rows_buffer.get()+row_buffer_pos*TotalColumnSizePtr+cols_sizes[col_num-1].second;
545 WOConnectionBase::WOConnectionBase(
const tstring &cnx,
const SQLUINTEGER timeout) : RecordsetBase(cnx,
false, timeout), in_transaction(
false) {
546 SQLUSMALLINT tx_level;
547 SQLSMALLINT StringLengthPtr;
548 SQLRETURN ret=SQLGetInfo(hdbc, SQL_TXN_CAPABLE,
reinterpret_cast<SQLPOINTER>(&tx_level),
sizeof(SQLUSMALLINT), &StringLengthPtr);
549 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
550 throw Database::ODBC::Exceptions::WOConnectionBaseErr(_T(
"Failed to find transaction support information on the connection handle."), &hdbc, SQL_HANDLE_DBC, ret);
552 if (tx_level==SQL_TC_NONE) {
553 throw Database::ODBC::Exceptions::WOConnectionBaseErr(_T(
"Transactions (which this applicationrequires) are not supported at all on the data base connection driver."), &hdbc, SQL_HANDLE_DBC, ret);
555 SQLTCHAR many_txs[]=_T(
" \0");
556 ret=SQLGetInfo(hdbc, SQL_MULTIPLE_ACTIVE_TXN,
reinterpret_cast<SQLPOINTER>(&many_txs),
sizeof(many_txs), &StringLengthPtr);
557 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
558 throw Database::ODBC::Exceptions::WOConnectionBaseErr(_T(
"Failed to find multiple-transaction support information on the data base connection driver."), &hdbc, SQL_HANDLE_DBC, ret);
560 if (many_txs[0]!=
'Y') {
561 throw Database::ODBC::Exceptions::WOConnectionBaseErr(_T(
"Multiple transactions are not supported at all on the data base connection driver."), &hdbc, SQL_HANDLE_DBC, ret);
566 WOConnectionBase::~WOConnectionBase(
void) {
567 if (in_transaction) {
573 void __fastcall WOConnectionBase::commit(
void) {
574 const SQLRETURN ret=SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_COMMIT);
575 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
576 throw Database::ODBC::Exceptions::WOConnectionBaseErr(_T(
"Failed to commit a transaction on the connection handle."), &hdbc, SQL_HANDLE_DBC, ret);
578 in_transaction=
false;
582 void __fastcall WOConnectionBase::rollback(
void) {
583 const SQLRETURN ret=SQLEndTran(SQL_HANDLE_DBC, hdbc, SQL_ROLLBACK);
584 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
585 throw Database::ODBC::Exceptions::WOConnectionBaseErr(_T(
"Failed to rollback a transaction on the connection handle."), &hdbc, SQL_HANDLE_DBC, ret);
587 in_transaction=
false;
591 SQLINTEGER
__fastcall WOConnectionBase::count(
void) {
592 RecordsetBase::RowsAffected();
593 return RecordsetBase::count();
597 bool __fastcall WOConnectionBase::in_trans(
void)
const {
598 return in_transaction;
603 void __fastcall WOConnectionBase::AddExecData(
const SQLUSMALLINT col_num,
const str_data_at_exec_type &data) {
604 exec_data[col_num]=data;
609 WOConnectionBase::str_data_at_exec_type &
__fastcall WOConnectionBase::ExecData(
const SQLUSMALLINT col_num) {
610 return exec_data[col_num];
614 WOConnection::WOConnection(
const tstring &cnx,
const SQLUINTEGER timeout) : WOConnectionBase(cnx, timeout) {
618 WOConnection::~WOConnection(
void) {
622 void __fastcall WOConnection::execute(
const tstring &sql) {
623 assert(!in_transaction);
627 WOConnectionBase::execute(sql);
632 WOBulkConnection::WOBulkConnection(
const tstring &cnx,
const SQLUINTEGER row_arr_sz,
const SQLUINTEGER timeout) : WOConnectionBase(cnx, timeout), row_buffer_size(row_arr_sz), prepared(
false) {
633 SQLUSMALLINT property;
634 SQLSMALLINT StringLengthPtr;
635 SQLRETURN ret=SQLGetInfo(hdbc, SQL_CURSOR_COMMIT_BEHAVIOR,
reinterpret_cast<SQLPOINTER>(&property),
sizeof(SQLUSMALLINT), &StringLengthPtr);
636 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
637 throw Database::ODBC::Exceptions::WOConnectionBaseErr(_T(
"Failed to get the prepared statement behaviour after commits on the connection handle."), &hdbc, SQL_HANDLE_DBC, ret);
639 sp_behaviour.commit=(property!=SQL_CB_DELETE);
640 ret=SQLGetInfo(hdbc, SQL_CURSOR_ROLLBACK_BEHAVIOR,
reinterpret_cast<SQLPOINTER>(&property),
sizeof(SQLUSMALLINT), &StringLengthPtr);
641 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
642 throw Database::ODBC::Exceptions::WOConnectionBaseErr(_T(
"Failed to get the prepared statement behaviour after rollbacks on the connection handle."), &hdbc, SQL_HANDLE_DBC, ret);
644 sp_behaviour.rollback=(property!=SQL_CB_DELETE);
648 WOBulkConnection::~WOBulkConnection(
void) {
652 void __fastcall WOBulkConnection::prepare(
const tstring &sql) {
653 assert(!in_transaction);
654 if (in_transaction) {
655 throw Database::ODBC::Exceptions::WOConnectionBaseErr(_T(
"Already in a transaction on the connection handle - it must be committed first."), sql_statement, &hdbc, SQL_HANDLE_DBC, SQL_ERROR);
658 const SQLRETURN ret=SQLFreeStmt(hstmt, SQL_RESET_PARAMS);
659 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
660 throw Database::ODBC::Exceptions::WOConnectionBaseErr(_T(
"Failed to unbind the previously bound paramaters on the SQL statement on the statement handle."), sql_statement, &hstmt, SQL_HANDLE_STMT, ret);
663 if (sql!=sql_statement){
668 SQLRETURN ret=SQLPrepare(hstmt,
const_cast<SQLTCHAR *>(sql.c_str()), SQL_NTS);
669 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
670 throw Database::ODBC::Exceptions::WOConnectionBaseErr(_T(
"Failed to prepare the SQL statement on the statement handle."), sql_statement, &hstmt, SQL_HANDLE_STMT, ret);
678 void __fastcall WOBulkConnection::execute(
void) {
680 assert(!in_transaction);
681 if (in_transaction) {
682 throw Database::ODBC::Exceptions::WOConnectionBaseErr(_T(
"Already in a transaction on the connection handle - it must be committed first."), sql_statement, &hdbc, SQL_HANDLE_DBC, SQL_ERROR);
684 SQLRETURN ret=SQLExecute(hstmt);
685 if (ret==SQL_NEED_DATA) {
687 while ((ret=SQLParamData(hstmt, &pToken))==SQL_NEED_DATA) {
688 ret=SQLPutData(hstmt, exec_data[
reinterpret_cast<SQLUSMALLINT>(pToken)].str->begin(), SQL_NTS);
689 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
690 throw Database::ODBC::Exceptions::WOConnectionBaseErr(
reinterpret_cast<SQLUSMALLINT>(pToken),
typeid(*(exec_data[
reinterpret_cast<SQLUSMALLINT>(pToken)].str)), _T(
"Failed to bung some data into the prepared, but hungry SQL statement on the statement handle."), sql_statement, &hstmt, SQL_HANDLE_STMT, ret);
694 if (ret!=SQL_SUCCESS && ret!=SQL_SUCCESS_WITH_INFO) {
695 throw Database::ODBC::Exceptions::WOConnectionBaseErr(_T(
"Failed to execute the prepared SQL statement on the statement handle."), sql_statement, &hstmt, SQL_HANDLE_STMT, ret);
701 void __fastcall WOBulkConnection::commit(
void) {
702 WOConnectionBase::commit();
703 if (!sp_behaviour.commit) {
705 prepare(sql_statement);
710 void __fastcall WOBulkConnection::rollback(
void) {
711 WOConnectionBase::rollback();
712 if (!sp_behaviour.rollback) {
714 prepare(sql_statement);