From 887ebfa69061c58814f58c7130ad3391291f81e5 Mon Sep 17 00:00:00 2001 From: Alex Kwiatkowski Date: Wed, 19 Jul 2023 01:05:30 -0700 Subject: [PATCH] wip: extract exception function - [ ] use go style call and check error flow - [ ] create template for SQL_OK --- src/include/exception.hpp | 64 +++++++++++++++++++++++ src/include/odbc.hpp | 103 +++++++++++--------------------------- 2 files changed, 92 insertions(+), 75 deletions(-) create mode 100644 src/include/exception.hpp diff --git a/src/include/exception.hpp b/src/include/exception.hpp new file mode 100644 index 0000000..7179723 --- /dev/null +++ b/src/include/exception.hpp @@ -0,0 +1,64 @@ +#pragma once + +#include "duckdb.hpp" +#include "duckdb/function/table_function.hpp" + +#include "sql.h" +#include "sqlext.h" + +namespace duckdb { +struct OdbcDiagnostics { + std::string msg; + std::string state; + SQLINTEGER native; +}; + +static unique_ptr ExtractDiagnostics(SQLSMALLINT handle_type, SQLHANDLE handle) { + SQLINTEGER i = 1; + SQLINTEGER native; + SQLCHAR state[7]; + SQLCHAR text[256]; + SQLSMALLINT len; + SQLRETURN return_code; + auto diagnostics = make_uniq(); + + while (SQL_SUCCEEDED( + SQLGetDiagRec(handle_type, handle, i, state, &native, text, sizeof(text), &len))) { + diagnostics->msg += string((char *)text); + diagnostics->state = string((char *)state); + diagnostics->native = native; + i++; + } + + return std::move(diagnostics); +} + +static std::string SqlReturnCodeToString(SQLRETURN return_code) { + switch (return_code) { + case SQL_SUCCESS: + return "SQL_SUCCESS"; + case SQL_SUCCESS_WITH_INFO: + return "SQL_SUCCESS_WITH_INFO"; + case SQL_NO_DATA: + return "SQL_NO_DATA"; + case SQL_ERROR: + return "SQL_ERROR"; + case SQL_INVALID_HANDLE: + return "SQL_INVALID_HANDLE"; + case SQL_STILL_EXECUTING: + return "SQL_STILL_EXECUTING"; + case SQL_NEED_DATA: + return "SQL_NEED_DATA"; + default: + return "UNKNOWN"; + } +} + +static void ThrowExceptionWithDiagnostics(std::string msg_prefix, SQLHANDLE handle, + SQLRETURN return_code) { + auto diagnostics = ExtractDiagnostics(SQL_HANDLE_STMT, handle); + throw Exception(msg_prefix + " return_code=" + std::to_string(return_code) + ":" + + SqlReturnCodeToString(return_code) + " msg='" + diagnostics->msg + "' state=" + + diagnostics->state + " native=" + std::to_string(diagnostics->native)); +} +} // namespace duckdb diff --git a/src/include/odbc.hpp b/src/include/odbc.hpp index 226b326..411b6fe 100644 --- a/src/include/odbc.hpp +++ b/src/include/odbc.hpp @@ -1,14 +1,13 @@ #pragma once -#include "duckdb.hpp" +#include "exception.hpp" +#include "duckdb.hpp" #include "duckdb/function/table_function.hpp" #include "sql.h" #include "sqlext.h" -#include - namespace duckdb { struct OdbcEnvironment { OdbcEnvironment() { handle = SQL_NULL_HENV; } @@ -18,100 +17,43 @@ struct OdbcEnvironment { void FreeHandle() { if (handle != SQL_NULL_HENV) { - auto sql_return = SQLFreeHandle(SQL_HANDLE_ENV, handle); - if (sql_return == SQL_SUCCESS) { - return; - } else if (sql_return == SQL_INVALID_HANDLE) { - throw Exception("OdbcEnvironment->Init() SQLFreeHandle SQL_RETURN=" + - std::to_string(sql_return) + " SQL_INVALID_HANDLE"); - } else if (sql_return == SQL_ERROR) { - throw Exception("OdbcEnvironment->Init() SQLFreeHandle SQL_RETURN=" + - std::to_string(sql_return) + " SQL_ERROR"); - } else { - throw Exception("OdbcEnvironment->Init() SQLFreeHandle SQL_RETURN=" + - std::to_string(sql_return) + " UNKNOWN_SQL_RETURN"); + auto return_code = SQLFreeHandle(SQL_HANDLE_ENV, handle); + if (!SQL_SUCCEEDED(return_code)) { + ThrowExceptionWithDiagnostics("OdbcEnvironment->Init() SQLFreeHandle", handle, return_code); } } } public: void Init() { + SQLRETURN return_code; + if (handle != SQL_NULL_HENV) { throw Exception("OdbcEnvironment->Init() handle is not null"); } - auto alloc_sql_return = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HENV, &handle); - if (alloc_sql_return == SQL_SUCCESS || alloc_sql_return == SQL_SUCCESS_WITH_INFO) { + return_code = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HENV, &handle); + if (return_code == SQL_SUCCESS || return_code == SQL_SUCCESS_WITH_INFO) { // ok - } else if (alloc_sql_return == SQL_INVALID_HANDLE) { + } else if (return_code == SQL_INVALID_HANDLE) { throw Exception("OdbcEnvironment->Init() SQLAllocHandle SQL_RETURN=" + - std::to_string(alloc_sql_return) + " SQL_INVALID_HANDLE"); - } else if (alloc_sql_return == SQL_ERROR) { + std::to_string(return_code) + " SQL_INVALID_HANDLE"); + } else if (return_code == SQL_ERROR) { throw Exception("OdbcEnvironment->Init() SQLAllocHandle SQL_RETURN=" + - std::to_string(alloc_sql_return) + " SQL_ERROR"); + std::to_string(return_code) + " SQL_ERROR"); } else { throw Exception("OdbcEnvironment->Init() SQLAllocHandle SQL_RETURN=" + - std::to_string(alloc_sql_return) + " UNKNOWN_SQL_RETURN"); + std::to_string(return_code) + " UNKNOWN_SQL_RETURN"); } - auto set_env_sql_return = - SQLSetEnvAttr(handle, SQL_ATTR_ODBC_VERSION, (SQLPOINTER *)SQL_OV_ODBC3, 0); - if (set_env_sql_return == SQL_SUCCESS) { - return; - } else if (set_env_sql_return == SQL_INVALID_HANDLE) { - throw Exception("OdbcEnvironment->Init() SQLSetEnvAttr SQL_RETURN=" + - std::to_string(set_env_sql_return) + " SQL_INVALID_HANDLE"); - } else if (set_env_sql_return == SQL_ERROR) { - throw Exception("OdbcEnvironment->Init() SQLSetEnvAttr SQL_RETURN=" + - std::to_string(set_env_sql_return) + " SQL_ERROR"); - } else { - throw Exception("OdbcEnvironment->Init() SQLSetEnvAttr SQL_RETURN=" + - std::to_string(set_env_sql_return) + " UNKNOWN_SQL_RETURN"); + return_code = SQLSetEnvAttr(handle, SQL_ATTR_ODBC_VERSION, (SQLPOINTER *)SQL_OV_ODBC3, 0); + if (!SQL_SUCCEEDED(return_code)) { + ThrowExceptionWithDiagnostics("OdbcEnvironment->Init() SQLSetEnvAttr", handle, return_code); } } SQLHENV Handle() const { return handle; } }; -#define MAXCOLS 1024 - -struct OdbcColumnDescription { - SQLCHAR name[32]; - SQLSMALLINT name_length; - SQLSMALLINT sql_data_type; - SQLSMALLINT c_data_type; - SQLULEN size; - SQLULEN length; - SQLSMALLINT decimal_digits; - SQLSMALLINT nullable; -}; - -struct OdbcDiagnostics { - string msg; - string state; - SQLINTEGER native; -}; - -static unique_ptr ExtractDiagnostics(SQLSMALLINT handle_type, SQLHANDLE handle) { - SQLINTEGER i = 0; - SQLINTEGER native; - SQLCHAR state[7]; - SQLCHAR text[256]; - SQLSMALLINT len; - SQLRETURN diag_return; - auto diagnostics = make_uniq(); - - do { - diag_return = SQLGetDiagRec(handle_type, handle, ++i, state, &native, text, sizeof(text), &len); - if (SQL_SUCCEEDED(diag_return)) { - diagnostics->msg += string((char *)text); - diagnostics->state = string((char *)state); - diagnostics->native = native; - } - } while (diag_return == SQL_SUCCESS); - - return std::move(diagnostics); -} - #define MAX_CONN_STR_OUT 1024 struct OdbcConnection { @@ -238,6 +180,17 @@ struct OdbcConnection { SQLHSTMT HandleConn() { return handle_conn; } }; +struct OdbcColumnDescription { + SQLCHAR name[32]; + SQLSMALLINT name_length; + SQLSMALLINT sql_data_type; + SQLSMALLINT c_data_type; + SQLULEN size; + SQLULEN length; + SQLSMALLINT decimal_digits; + SQLSMALLINT nullable; +}; + struct OdbcStatementOptions { OdbcStatementOptions(SQLULEN _row_array_size) : row_array_size(_row_array_size) {}