// //////////////////////////////////////////////////////////////////////////// SQLSMALLINT GetDiagRecordNumberCount(SQLSMALLINT handle_type, SQLHANDLE handle_value) { SQLINTEGER record_number; try { GetDiagField_SQLINTEGER(handle_type, handle_value, 0, SQL_DIAG_NUMBER, record_number); if (record_number < 0) throw OdbcException("Invocation of '::SQLGetDiagField()' with a " "diagnostic identifier of 'SQL_DIAG_NUMBER' resulted in a value " "of 0, which is invalid."); else if (record_number > std::numeric_limits<SQLSMALLINT>::max()) throw OdbcException("Invocation of '::SQLGetDiagField()' with a " "diagnostic identifier of 'SQL_DIAG_NUMBER' resulted in a value " "of " + MLB::Utility::AnyToString(record_number) + ", which is " "greater than the maximum permissible for a value of type '" "SQLSMALLINT' (" + MLB::Utility::AnyToString( std::numeric_limits<SQLSMALLINT>::max()) + ")."); } catch (const std::exception &except) { MLB::Utility::Rethrow(except, "Attempt to retrieve the count of " "diagnostic records for handle type " + MLB::Utility::AnyToString(handle_type) + ", handle value " + MLB::Utility::AnyToString(handle_value) + " failed: " + std::string(except.what())); } return(static_cast<SQLSMALLINT>(record_number)); }
// //////////////////////////////////////////////////////////////////////////// SQLRETURN GetAttrForHandle(SQLHANDLE Handle, SQLINTEGER Attribute, std::string &ValuePtr, const char *odbc_function_name, HandleGetAttrFunc get_attr_function, OdbcThrowFlags::Flags throw_flags) { CheckStringArgInLengthDomain<SQLINTEGER>(ValuePtr, "ValuePtr", odbc_function_name); SQLRETURN return_code; MLB::Utility::IncrementalBuffer<SQLCHAR, 4096> buffer; throw_flags = MLB::Utility::EnumFlagAnd(throw_flags, static_cast<OdbcThrowFlags::Flags>(~(OdbcThrowFlags::SuccessWithInfo))); for ( ; ; ) { SQLINTEGER actual_len; return_code = get_attr_function(Handle, Attribute, reinterpret_cast<SQLPOINTER>(buffer.GetPtr()), buffer.GetAllocationCountAsType<SQLINTEGER>(), &actual_len, throw_flags); if (return_code == SQL_SUCCESS) { std::string(reinterpret_cast<const char *>(buffer.GetPtr()), actual_len).swap(ValuePtr); break; } else if (return_code == SQL_SUCCESS_WITH_INFO) { if (!buffer.SetCount(actual_len, false)) throw OdbcException("Call to '" + std::string(odbc_function_name) + "' returned 'SQL_SUCCESS_WITH_INFO', but no returned lengths " "qualified for that return code."); } } return(return_code); }
bool OdbcConnection::TryReadRow() { if (executionState != FetchRow) { throw OdbcException("The connection is in an invalid state"); } SQLRETURN ret = SQLFetch(statement); if (ret == SQL_STILL_EXECUTING) { return false; } if (ret == SQL_NO_DATA) { resultset->moreRows = false; statement.Free(); executionState = Idle; return true; } else { resultset->moreRows = true; } if (!SQL_SUCCEEDED(ret)) { statement.Throw(); } return true; }
// //////////////////////////////////////////////////////////////////////////// SQLSMALLINT CheckIsValidDiagRecordNumber(SQLSMALLINT handle_type, SQLHANDLE handle_value, SQLSMALLINT record_number) { if (record_number < 1) throw OdbcException("A diagnostic record number of 0 is invalid (" "diagnostic records are numbered from 1)."); SQLSMALLINT record_number_count = GetDiagRecordNumberCount(handle_type, handle_value); if (record_number > record_number_count) throw OdbcException("The specified diagnostic record number of " + MLB::Utility::AnyToString(record_number) + "handle type " + MLB::Utility::AnyToString(handle_type) + ", handle value " + MLB::Utility::AnyToString(handle_value) + " is invalid --- " "the maximum available diagnostic record number is " + MLB::Utility::AnyToString(record_number_count) + "."); return(record_number_count); }
// //////////////////////////////////////////////////////////////////////////// const GetInfoTypeDatumRaw *GetInfoTypeDatumRaw::GetInfoTypeDatumPtrChecked( SQLUSMALLINT info_type) { const GetInfoTypeDatumRaw *found_ptr = GetInfoTypeDatumPtr(info_type); if (found_ptr == NULL) throw OdbcException("Unable to locate the '::SQLGetInfo()' information " "type descriptor entry for " + MLB::Utility::AnyToString(info_type) + "."); return(found_ptr); }
void OdbcConnection::InitializeEnvironment() { SQLRETURN ret = SQLSetEnvAttr(NULL, SQL_ATTR_CONNECTION_POOLING, (SQLPOINTER)SQL_CP_ONE_PER_HENV, 0); if (!SQL_SUCCEEDED(ret)) { throw OdbcException("Unable to initialize ODBC connection pooling"); } environment.Alloc(); ret = SQLSetEnvAttr(environment, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0); if (!SQL_SUCCEEDED(ret)) { throw OdbcException::Create(SQL_HANDLE_ENV, environment); } ret = SQLSetEnvAttr(environment, SQL_ATTR_CP_MATCH, (SQLPOINTER)SQL_CP_RELAXED_MATCH, 0); if (!SQL_SUCCEEDED(ret)) { throw OdbcException::Create(SQL_HANDLE_ENV, environment); } }
void COdbcEnvironment::InitializeEnvironment( ) { SetEnvAttr( SQL_ATTR_CONNECTION_POOLING, ( SQLPOINTER )SQL_CP_DRIVER_AWARE ); if( !Alloc( ) ) { throw OdbcException( GetSqlError( ) ); } if( !SetEnvAttr( SQL_ATTR_ODBC_VERSION, ( SQLPOINTER )SQL_OV_ODBC3_80 ) ) { if( !SetEnvAttr( SQL_ATTR_ODBC_VERSION, ( SQLPOINTER )SQL_OV_ODBC3 ) ) { throw OdbcException( GetSqlError( ) ); } } if( !SetEnvAttr( SQL_ATTR_CP_MATCH, ( SQLPOINTER )SQL_CP_RELAXED_MATCH ) ) { throw OdbcException( GetSqlError( ) ); } }
// //////////////////////////////////////////////////////////////////////////// SQLRETURN DescribeCol(SQLHSTMT StatementHandle, SQLSMALLINT ColumnNumber, std::string &ColumnName, SQLSMALLINT *DataTypePtr, SQLUINTEGER *ColumnSizePtr, SQLSMALLINT *DecimalDigitsPtr, SQLSMALLINT *NullablePtr, OdbcThrowFlags::Flags throw_flags) { SQLRETURN return_code; MLB::Utility::IncrementalBuffer<SQLCHAR, 256> buffer; OdbcThrowFlags::Flags tmp_throw_flags; tmp_throw_flags = MLB::Utility::EnumFlagAnd(throw_flags, static_cast<OdbcThrowFlags::Flags>(~(OdbcThrowFlags::SuccessWithInfo))); for ( ; ; ) { SQLSMALLINT actual_len; return_code = DescribeCol(StatementHandle, ColumnNumber, reinterpret_cast<SQLCHAR *>(buffer.GetPtr()), buffer.GetAllocationCountAsType<SQLSMALLINT>(), &actual_len, DataTypePtr, ColumnSizePtr, DecimalDigitsPtr, NullablePtr, tmp_throw_flags); if (return_code == SQL_SUCCESS) { std::string(reinterpret_cast<const char *>(buffer.GetPtr()), actual_len).swap(ColumnName); break; } else if (return_code == SQL_SUCCESS_WITH_INFO) { if (!buffer.SetCount(actual_len, false)) { std::string(reinterpret_cast<const char *>(buffer.GetPtr()), actual_len).swap(ColumnName); if (throw_flags & OdbcThrowFlags::SuccessWithInfo) throw OdbcException("Call to '::SQLDescribeCol()' returned " "'SQL_SUCCESS_WITH_INFO', but no returned lengths qualified " "for that return code."); break; } } } return(return_code); }
bool OdbcConnection::TryOpen(const wstring& connectionString) { SQLRETURN ret; if (connectionState == Closed) { OdbcConnectionHandle localConnection; localConnection.Alloc(environment); this->connection = std::move(localConnection); // TODO: determine async open support correctly // SQLSetConnectAttr(connection, SQL_ATTR_ASYNC_DBC_FUNCTIONS_ENABLE, (SQLPOINTER)SQL_ASYNC_DBC_ENABLE_ON, 0); connectionState = Opening; } if (connectionState == Opening) { ret = SQLDriverConnect(connection, NULL, const_cast<wchar_t*>(connectionString.c_str()), connectionString.length(), NULL, 0, NULL, SQL_DRIVER_NOPROMPT); if (ret == SQL_STILL_EXECUTING) { return false; } if (!SQL_SUCCEEDED(ret)) { connection.Throw(); } connectionState = Open; return true; } throw OdbcException("Attempt to open a connection that is not closed"); }
bool OdbcConnection::TryExecute(const wstring& query) { if (connectionState != Open) { throw OdbcException("Unable to execute a query on a connection that is not open"); } if (executionState == Idle) { statement.Alloc(connection); // ignore failure - optional attribute SQLSetStmtAttr(statement, SQL_ATTR_ASYNC_ENABLE, (SQLPOINTER)SQL_ASYNC_ENABLE_ON, 0); executionState = Executing; } if (executionState == Executing) { SQLRETURN ret = SQLExecDirect(statement, const_cast<wchar_t*>(query.c_str()), query.length()); if (ret == SQL_STILL_EXECUTING) { return false; } if (!SQL_SUCCEEDED(ret)) { statement.Throw(); } executionState = CountingColumns; } if (executionState == CountingColumns) { SQLSMALLINT columns; SQLRETURN ret = SQLNumResultCols(statement, &columns); if (ret == SQL_STILL_EXECUTING) { return false; } if (!SQL_SUCCEEDED(ret)) { statement.Throw(); } executionState = Metadata; column = 0; resultset = make_shared<ResultSet>(columns); } if (executionState == Metadata) { while (column < resultset->GetColumns()) { SQLSMALLINT nameLength; SQLRETURN ret = SQLDescribeCol(statement, column + 1, nullptr, 0, &nameLength, nullptr, nullptr, nullptr, nullptr); if (ret == SQL_STILL_EXECUTING) { return false; } if (!SQL_SUCCEEDED(ret)) { statement.Throw(); } ResultSet::ColumnDefinition& current = resultset->GetMetadata(column); vector<wchar_t> buffer(nameLength+1); ret = SQLDescribeCol(statement, column + 1, buffer.data(), nameLength+1, &nameLength, ¤t.dataType, ¤t.columnSize, ¤t.decimalDigits, ¤t.nullable); if (ret == SQL_STILL_EXECUTING) { return false; } if (!SQL_SUCCEEDED(ret)) { statement.Throw(); } current.name = wstring(buffer.data(), nameLength); column++; } executionState = CountRows; } if (executionState == CountRows) { SQLLEN rowcount; SQLRETURN ret = SQLRowCount(statement, &rowcount); if (!SQL_SUCCEEDED(ret)) { statement.Throw(); } resultset->rowcount = rowcount; if (resultset->GetColumns() > 0) { executionState = FetchRow; } else { statement.Free(); executionState = Idle; } return true; } throw OdbcException("The connection is in an invalid state"); }
bool OdbcConnection::TryReadColumn(int column) { if (executionState != FetchRow) { throw OdbcException("The connection is in an invalid state"); } if (column < 0 || column >= resultset->GetColumns()) { // TODO report an error return true; } SQLLEN strLen_or_IndPtr; const ResultSet::ColumnDefinition& definition = resultset->GetMetadata(column); switch (definition.dataType) { case SQL_CHAR: case SQL_VARCHAR: case SQL_LONGVARCHAR: case SQL_WCHAR: case SQL_WVARCHAR: case SQL_WLONGVARCHAR: { bool more = false; wchar_t buffer[2048+1] = {0}; SQLRETURN ret = SQLGetData(statement, column + 1, SQL_C_WCHAR, buffer, sizeof(buffer)-sizeof(wchar_t), &strLen_or_IndPtr); if (ret == SQL_STILL_EXECUTING) { return false; } if (!SQL_SUCCEEDED(ret)) { statement.Throw(); } if (strLen_or_IndPtr == SQL_NULL_DATA) { resultset->SetColumn(make_shared<NullColumn>()); } else { SQLWCHAR SQLState[6]; SQLINTEGER nativeError; SQLSMALLINT textLength; if (ret == SQL_SUCCESS_WITH_INFO) { ret = SQLGetDiagRec(SQL_HANDLE_STMT, statement, 1, SQLState, &nativeError, NULL, 0, &textLength); if (!SQL_SUCCEEDED(ret)) { statement.Throw(); } more = wcsncmp(SQLState, L"01004", 6) == 0; } resultset->SetColumn(make_shared<StringColumn>(buffer, more)); } } break; case SQL_SMALLINT: case SQL_BIT: case SQL_TINYINT: case SQL_INTEGER: { long val; SQLRETURN ret = SQLGetData(statement, column + 1, SQL_C_SLONG, &val, sizeof(val), &strLen_or_IndPtr); if (ret == SQL_STILL_EXECUTING) { return false; } if (!SQL_SUCCEEDED(ret)) { statement.Throw(); } if (strLen_or_IndPtr == SQL_NULL_DATA) { resultset->SetColumn(make_shared<NullColumn>()); } else { resultset->SetColumn(make_shared<IntColumn>(val)); } } break; case SQL_DECIMAL: case SQL_NUMERIC: case SQL_REAL: case SQL_FLOAT: case SQL_DOUBLE: case SQL_BIGINT: { double val; SQLRETURN ret = SQLGetData(statement, column + 1, SQL_C_DOUBLE, &val, sizeof(val), &strLen_or_IndPtr); if (ret == SQL_STILL_EXECUTING) { return false; } if (!SQL_SUCCEEDED(ret)) { statement.Throw(); } if (strLen_or_IndPtr == SQL_NULL_DATA) { resultset->SetColumn(make_shared<NullColumn>()); } else { resultset->SetColumn(make_shared<NumberColumn>(val)); } } break; case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: { bool more = false; vector<char> buffer(2048); SQLRETURN ret = SQLGetData(statement, column + 1, SQL_C_BINARY, buffer.data(), buffer.size(), &strLen_or_IndPtr); if (ret == SQL_STILL_EXECUTING) { return false; } if (!SQL_SUCCEEDED(ret)) { statement.Throw(); } if (strLen_or_IndPtr == SQL_NULL_DATA) { resultset->SetColumn(make_shared<NullColumn>()); } else { assert(strLen_or_IndPtr != SQL_NO_TOTAL); // per http://msdn.microsoft.com/en-us/library/windows/desktop/ms715441(v=vs.85).aspx SQLWCHAR SQLState[6]; SQLINTEGER nativeError; SQLSMALLINT textLength; if (ret == SQL_SUCCESS_WITH_INFO) { ret = SQLGetDiagRec(SQL_HANDLE_STMT, statement, 1, SQLState, &nativeError, NULL, 0, &textLength); if (!SQL_SUCCEEDED(ret)) { statement.Throw(); } more = wcsncmp(SQLState, L"01004", 6) == 0; } int amount = strLen_or_IndPtr; if (more) { amount = buffer.size(); } vector<char> trimmed(amount); memcpy(trimmed.data(), buffer.data(), amount); resultset->SetColumn(make_shared<BinaryColumn>(trimmed, more)); } } break; // use text format form time/date/etc.. for now // INTERVAL TYPES? case SQL_GUID: case SQL_TYPE_TIME: case SQL_TYPE_TIMESTAMP: case SQL_TYPE_DATE: default: { // TODO: how to figure out the size? vector<wchar_t> buffer(8192+1); SQLRETURN ret = SQLGetData(statement, column + 1, SQL_C_WCHAR, buffer.data(), 8192*sizeof(wchar_t), &strLen_or_IndPtr); if (ret == SQL_STILL_EXECUTING) { return false; } if (!SQL_SUCCEEDED(ret)) { statement.Throw(); } resultset->SetColumn(make_shared<StringColumn>(buffer.data(), false)); } break; } return true; }