#ifndef NALL_ODBC_HPP #define NALL_ODBC_HPP //minimal wrapper for core ODBC v3 API //requires Windows or unixODBC #include #include #include #include #include namespace nall { struct ODBC { inline ODBC(); inline ODBC(const string& database, const string& username, const string& password); inline ~ODBC(); inline bool connected(); inline bool connect(const string& hostname, const string& username, const string& password); inline void disconnect(); template inline bool execute(Args&&... args); inline void release(); inline unsigned rows(); inline lstring read(); private: char* buffer = nullptr; SQLHANDLE sqlEnvironment = nullptr; SQLHANDLE sqlConnection = nullptr; SQLHANDLE sqlStatement = nullptr; }; ODBC::ODBC() { auto result = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &sqlEnvironment); if(result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) return; SQLSetEnvAttr(sqlEnvironment, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); buffer = new char[65536](); } ODBC::ODBC(const string& database, const string& username, const string& password) : ODBC() { connect(database, username, password); } ODBC::~ODBC() { if(sqlEnvironment) { disconnect(); SQLFreeHandle(SQL_HANDLE_ENV, sqlEnvironment); sqlEnvironment = nullptr; delete[] buffer; buffer = nullptr; } } bool ODBC::connected() { return sqlConnection; } bool ODBC::connect(const string& hostname, const string& username, const string& password) { if(!sqlEnvironment) return false; disconnect(); auto result = SQLAllocHandle(SQL_HANDLE_DBC, sqlEnvironment, &sqlConnection); if(result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) return false; SQLSetConnectAttr(sqlConnection, SQL_LOGIN_TIMEOUT, (SQLPOINTER)5, 0); result = SQLConnectA(sqlConnection, (SQLCHAR*)(const char*)hostname, SQL_NTS, (SQLCHAR*)(const char*)username, SQL_NTS, (SQLCHAR*)(const char*)password, SQL_NTS ); if(result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { disconnect(); return false; } return true; } void ODBC::disconnect() { if(sqlConnection) { release(); SQLDisconnect(sqlConnection); SQLFreeHandle(SQL_HANDLE_DBC, sqlConnection); sqlConnection = nullptr; } } template bool ODBC::execute(Args&&... args) { string statement({args...}); if(!sqlConnection) return false; release(); auto result = SQLAllocHandle(SQL_HANDLE_STMT, sqlConnection, &sqlStatement); if(result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) return false; result = SQLExecDirectA(sqlStatement, (SQLCHAR*)(const char*)statement, SQL_NTS); if(result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { release(); return false; } return true; } void ODBC::release() { if(sqlStatement) { SQLFreeHandle(SQL_HANDLE_STMT, sqlStatement); sqlStatement = nullptr; } } //valid after update, insert or delete unsigned ODBC::rows() { if(!sqlStatement) return 0; SQLLEN sqlRows = 0; auto result = SQLRowCount(sqlStatement, &sqlRows); if(result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) return 0; return sqlRows; } //valid after select lstring ODBC::read() { if(!sqlStatement) return {}; auto result = SQLFetch(sqlStatement); if(result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) return {}; SQLSMALLINT sqlColumns = 0; result = SQLNumResultCols(sqlStatement, &sqlColumns); if(result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) return {}; lstring data; for(unsigned column = 0; column < sqlColumns; column++) { SQLLEN length = 0; SQLGetData(sqlStatement, 1 + column, SQL_C_CHAR, buffer, 65535, &length); data.append(buffer); } return data; } } #endif