From 0ae29939334e604ff14024e834cf55cf741d70c4 Mon Sep 17 00:00:00 2001 From: "Alina (Xi) Li" Date: Tue, 7 Oct 2025 15:51:43 -0700 Subject: [PATCH 1/3] Extract SQLGetDiagField and SQLGetDiagRec implementation Work on code review comments - remove `diag_info_ptr` from `IsValidStringFieldArgs` checks Co-Authored-By: rscales --- cpp/src/arrow/flight/sql/odbc/odbc_api.cc | 356 +++++++++++++++++++++- 1 file changed, 352 insertions(+), 4 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc index b4408d26565..01a9287613d 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc @@ -65,6 +65,20 @@ SQLRETURN SQLFreeStmt(SQLHSTMT handle, SQLUSMALLINT option) { return SQL_INVALID_HANDLE; } +inline bool IsValidStringFieldArgs(SQLPOINTER diag_info_ptr, SQLSMALLINT buffer_length, + SQLSMALLINT* string_length_ptr, bool is_unicode) { + const SQLSMALLINT char_size = is_unicode ? GetSqlWCharSize() : sizeof(char); + const bool has_valid_buffer = + buffer_length == SQL_NTS || (buffer_length >= 0 && buffer_length % char_size == 0); + + // regardless of capacity return false if invalid + if (diag_info_ptr && !has_valid_buffer) { + return false; + } + + return has_valid_buffer || string_length_ptr; +} + SQLRETURN SQLGetDiagField(SQLSMALLINT handle_type, SQLHANDLE handle, SQLSMALLINT rec_number, SQLSMALLINT diag_identifier, SQLPOINTER diag_info_ptr, SQLSMALLINT buffer_length, @@ -76,8 +90,258 @@ SQLRETURN SQLGetDiagField(SQLSMALLINT handle_type, SQLHANDLE handle, << ", diag_info_ptr: " << diag_info_ptr << ", buffer_length: " << buffer_length << ", string_length_ptr: " << static_cast(string_length_ptr); - // GH-46575 TODO: Implement SQLGetDiagField - return SQL_INVALID_HANDLE; + // GH-46575 TODO: Add tests for SQLGetDiagField + using ODBC::GetStringAttribute; + using ODBC::ODBCConnection; + using ODBC::ODBCDescriptor; + using ODBC::ODBCEnvironment; + using ODBC::ODBCStatement; + + if (!handle) { + return SQL_INVALID_HANDLE; + } + + if (!diag_info_ptr && !string_length_ptr) { + return SQL_ERROR; + } + + // If buffer length derived from null terminated string + if (diag_info_ptr && buffer_length == SQL_NTS) { + const wchar_t* str = reinterpret_cast(diag_info_ptr); + buffer_length = wcslen(str) * GetSqlWCharSize(); + } + + // Set character type to be Unicode by default + const bool is_unicode = true; + Diagnostics* diagnostics = nullptr; + + switch (handle_type) { + case SQL_HANDLE_ENV: { + ODBCEnvironment* environment = reinterpret_cast(handle); + diagnostics = &environment->GetDiagnostics(); + break; + } + + case SQL_HANDLE_DBC: { + ODBCConnection* connection = reinterpret_cast(handle); + diagnostics = &connection->GetDiagnostics(); + break; + } + + case SQL_HANDLE_DESC: { + ODBCDescriptor* descriptor = reinterpret_cast(handle); + diagnostics = &descriptor->GetDiagnostics(); + break; + } + + case SQL_HANDLE_STMT: { + ODBCStatement* statement = reinterpret_cast(handle); + diagnostics = &statement->GetDiagnostics(); + break; + } + + default: + return SQL_ERROR; + } + + if (!diagnostics) { + return SQL_ERROR; + } + + // Retrieve and return if header level diagnostics + switch (diag_identifier) { + case SQL_DIAG_NUMBER: { + if (diag_info_ptr) { + *static_cast(diag_info_ptr) = + static_cast(diagnostics->GetRecordCount()); + } + + if (string_length_ptr) { + *string_length_ptr = sizeof(SQLINTEGER); + } + + return SQL_SUCCESS; + } + + // Driver manager implements SQL_DIAG_RETURNCODE + case SQL_DIAG_RETURNCODE: { + return SQL_SUCCESS; + } + + case SQL_DIAG_CURSOR_ROW_COUNT: { + if (handle_type == SQL_HANDLE_STMT) { + if (diag_info_ptr) { + // Will always be 0 if only SELECT supported + *static_cast(diag_info_ptr) = 0; + } + + if (string_length_ptr) { + *string_length_ptr = sizeof(SQLLEN); + } + + return SQL_SUCCESS; + } + + return SQL_ERROR; + } + + // Not supported + case SQL_DIAG_DYNAMIC_FUNCTION: + case SQL_DIAG_DYNAMIC_FUNCTION_CODE: { + if (handle_type == SQL_HANDLE_STMT) { + return SQL_SUCCESS; + } + + return SQL_ERROR; + } + + case SQL_DIAG_ROW_COUNT: { + if (handle_type == SQL_HANDLE_STMT) { + if (diag_info_ptr) { + // Will always be 0 if only SELECT is supported + *static_cast(diag_info_ptr) = 0; + } + + if (string_length_ptr) { + *string_length_ptr = sizeof(SQLLEN); + } + + return SQL_SUCCESS; + } + + return SQL_ERROR; + } + } + + // If not a diagnostic header field then the record number must be 1 or greater + if (rec_number < 1) { + return SQL_ERROR; + } + + // Retrieve record level diagnostics from specified 1 based record + const uint32_t record_index = static_cast(rec_number - 1); + if (!diagnostics->HasRecord(record_index)) { + return SQL_NO_DATA; + } + + // Retrieve record field data + switch (diag_identifier) { + case SQL_DIAG_MESSAGE_TEXT: { + if (IsValidStringFieldArgs(diag_info_ptr, buffer_length, string_length_ptr, + is_unicode)) { + const std::string& message = diagnostics->GetMessageText(record_index); + return GetStringAttribute(is_unicode, message, true, diag_info_ptr, buffer_length, + string_length_ptr, *diagnostics); + } + + return SQL_ERROR; + } + + case SQL_DIAG_NATIVE: { + if (diag_info_ptr) { + *static_cast(diag_info_ptr) = + diagnostics->GetNativeError(record_index); + } + + if (string_length_ptr) { + *string_length_ptr = sizeof(SQLINTEGER); + } + + return SQL_SUCCESS; + } + + case SQL_DIAG_SERVER_NAME: { + if (IsValidStringFieldArgs(diag_info_ptr, buffer_length, string_length_ptr, + is_unicode)) { + switch (handle_type) { + case SQL_HANDLE_DBC: { + ODBCConnection* connection = reinterpret_cast(handle); + std::string dsn = connection->GetDSN(); + return GetStringAttribute(is_unicode, dsn, true, diag_info_ptr, buffer_length, + string_length_ptr, *diagnostics); + } + + case SQL_HANDLE_DESC: { + ODBCDescriptor* descriptor = reinterpret_cast(handle); + ODBCConnection* connection = &descriptor->GetConnection(); + std::string dsn = connection->GetDSN(); + return GetStringAttribute(is_unicode, dsn, true, diag_info_ptr, buffer_length, + string_length_ptr, *diagnostics); + break; + } + + case SQL_HANDLE_STMT: { + ODBCStatement* statement = reinterpret_cast(handle); + ODBCConnection* connection = &statement->GetConnection(); + std::string dsn = connection->GetDSN(); + return GetStringAttribute(is_unicode, dsn, true, diag_info_ptr, buffer_length, + string_length_ptr, *diagnostics); + } + + default: + return SQL_ERROR; + } + } + + return SQL_ERROR; + } + + case SQL_DIAG_SQLSTATE: { + if (IsValidStringFieldArgs(diag_info_ptr, buffer_length, string_length_ptr, + is_unicode)) { + const std::string& state = diagnostics->GetSQLState(record_index); + return GetStringAttribute(is_unicode, state, true, diag_info_ptr, buffer_length, + string_length_ptr, *diagnostics); + } + + return SQL_ERROR; + } + + // Return valid dummy variable for unimplemented field + case SQL_DIAG_COLUMN_NUMBER: { + if (diag_info_ptr) { + *static_cast(diag_info_ptr) = SQL_NO_COLUMN_NUMBER; + } + + if (string_length_ptr) { + *string_length_ptr = sizeof(SQLINTEGER); + } + + return SQL_SUCCESS; + } + + // Return empty string dummy variable for unimplemented fields + case SQL_DIAG_CLASS_ORIGIN: + case SQL_DIAG_CONNECTION_NAME: + case SQL_DIAG_SUBCLASS_ORIGIN: { + if (IsValidStringFieldArgs(diag_info_ptr, buffer_length, string_length_ptr, + is_unicode)) { + return GetStringAttribute(is_unicode, "", true, diag_info_ptr, buffer_length, + string_length_ptr, *diagnostics); + } + + return SQL_ERROR; + } + + // Return valid dummy variable for unimplemented field + case SQL_DIAG_ROW_NUMBER: { + if (diag_info_ptr) { + *static_cast(diag_info_ptr) = SQL_NO_ROW_NUMBER; + } + + if (string_length_ptr) { + *string_length_ptr = sizeof(SQLLEN); + } + + return SQL_SUCCESS; + } + + default: { + return SQL_ERROR; + } + } + + return SQL_ERROR; } SQLRETURN SQLGetDiagRec(SQLSMALLINT handle_type, SQLHANDLE handle, SQLSMALLINT rec_number, @@ -91,8 +355,92 @@ SQLRETURN SQLGetDiagRec(SQLSMALLINT handle_type, SQLHANDLE handle, SQLSMALLINT r << ", message_text: " << static_cast(message_text) << ", buffer_length: " << buffer_length << ", text_length_ptr: " << static_cast(text_length_ptr); - // GH-46575 TODO: Implement SQLGetDiagRec - return SQL_INVALID_HANDLE; + // GH-46575 TODO: Add tests for SQLGetDiagRec + using arrow::flight::sql::odbc::Diagnostics; + using ODBC::GetStringAttribute; + using ODBC::ODBCConnection; + using ODBC::ODBCDescriptor; + using ODBC::ODBCEnvironment; + using ODBC::ODBCStatement; + + if (!handle) { + return SQL_INVALID_HANDLE; + } + + // Record number must be greater or equal to 1 + if (rec_number < 1 || buffer_length < 0) { + return SQL_ERROR; + } + + // Set character type to be Unicode by default + const bool is_unicode = true; + Diagnostics* diagnostics = nullptr; + + switch (handle_type) { + case SQL_HANDLE_ENV: { + auto* environment = ODBCEnvironment::Of(handle); + diagnostics = &environment->GetDiagnostics(); + break; + } + + case SQL_HANDLE_DBC: { + auto* connection = ODBCConnection::Of(handle); + diagnostics = &connection->GetDiagnostics(); + break; + } + + case SQL_HANDLE_DESC: { + auto* descriptor = ODBCDescriptor::Of(handle); + diagnostics = &descriptor->GetDiagnostics(); + break; + } + + case SQL_HANDLE_STMT: { + auto* statement = ODBCStatement::Of(handle); + diagnostics = &statement->GetDiagnostics(); + break; + } + + default: + return SQL_INVALID_HANDLE; + } + + if (!diagnostics) { + return SQL_ERROR; + } + + // Convert from ODBC 1 based record number to internal diagnostics 0 indexed storage + const size_t record_index = static_cast(rec_number - 1); + if (!diagnostics->HasRecord(record_index)) { + return SQL_NO_DATA; + } + + if (sql_state) { + // The length of the sql state is always 5 characters plus null + SQLSMALLINT size = 6; + const std::string& state = diagnostics->GetSQLState(record_index); + + // Microsoft documentation does not mention + // any SQLGetDiagRec return value that is associated with `sql_state` buffer, so + // the return value for writing `sql_state` buffer is ignored by the driver. + GetStringAttribute(is_unicode, state, false, sql_state, size, &size, *diagnostics); + } + + if (native_error_ptr) { + *native_error_ptr = diagnostics->GetNativeError(record_index); + } + + if (message_text || text_length_ptr) { + const std::string& message = diagnostics->GetMessageText(record_index); + + // According to Microsoft documentation, + // SQL_SUCCESS_WITH_INFO should be returned if `*message_text` buffer was too + // small to hold the requested diagnostic message. + return GetStringAttribute(is_unicode, message, false, message_text, buffer_length, + text_length_ptr, *diagnostics); + } + + return SQL_SUCCESS; } SQLRETURN SQLGetEnvAttr(SQLHENV env, SQLINTEGER attr, SQLPOINTER value_ptr, From 34281ec1825b6bfe965bfe431aab61062fd42913 Mon Sep 17 00:00:00 2001 From: "Alina (Xi) Li" Date: Wed, 22 Oct 2025 13:44:18 -0700 Subject: [PATCH 2/3] Use `ARROW_UNUSED` for ignored results --- cpp/src/arrow/flight/sql/odbc/odbc_api.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc index 01a9287613d..ff0e2a4e7d8 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbc_api.cc +++ b/cpp/src/arrow/flight/sql/odbc/odbc_api.cc @@ -423,7 +423,8 @@ SQLRETURN SQLGetDiagRec(SQLSMALLINT handle_type, SQLHANDLE handle, SQLSMALLINT r // Microsoft documentation does not mention // any SQLGetDiagRec return value that is associated with `sql_state` buffer, so // the return value for writing `sql_state` buffer is ignored by the driver. - GetStringAttribute(is_unicode, state, false, sql_state, size, &size, *diagnostics); + ARROW_UNUSED(GetStringAttribute(is_unicode, state, false, sql_state, size, &size, + *diagnostics)); } if (native_error_ptr) { From 4a0fb9d34d1446b277f7c9db14e9157df6ce2ba5 Mon Sep 17 00:00:00 2001 From: "Alina (Xi) Li" Date: Wed, 22 Oct 2025 13:55:15 -0700 Subject: [PATCH 3/3] Add `[[nodiscard]]` for ODBC APIs Addresses community comment: https://github.com/apache/arrow/pull/47763/files#r2450014186 --- .../arrow/flight/sql/odbc/odbc_api_internal.h | 166 ++++++++++-------- 1 file changed, 91 insertions(+), 75 deletions(-) diff --git a/cpp/src/arrow/flight/sql/odbc/odbc_api_internal.h b/cpp/src/arrow/flight/sql/odbc/odbc_api_internal.h index 53273a5b6d3..4fea8569acb 100644 --- a/cpp/src/arrow/flight/sql/odbc/odbc_api_internal.h +++ b/cpp/src/arrow/flight/sql/odbc/odbc_api_internal.h @@ -27,79 +27,95 @@ // // Define internal ODBC API function headers. namespace arrow::flight::sql::odbc { -SQLRETURN SQLAllocHandle(SQLSMALLINT type, SQLHANDLE parent, SQLHANDLE* result); -SQLRETURN SQLFreeHandle(SQLSMALLINT type, SQLHANDLE handle); -SQLRETURN SQLFreeStmt(SQLHSTMT stmt, SQLUSMALLINT option); -SQLRETURN SQLGetDiagField(SQLSMALLINT handle_type, SQLHANDLE handle, - SQLSMALLINT rec_number, SQLSMALLINT diag_identifier, - SQLPOINTER diag_info_ptr, SQLSMALLINT buffer_length, - SQLSMALLINT* string_length_ptr); -SQLRETURN SQLGetDiagRec(SQLSMALLINT handle_type, SQLHANDLE handle, SQLSMALLINT rec_number, - SQLWCHAR* sql_state, SQLINTEGER* native_error_ptr, - SQLWCHAR* message_text, SQLSMALLINT buffer_length, - SQLSMALLINT* text_length_ptr); -SQLRETURN SQLGetEnvAttr(SQLHENV env, SQLINTEGER attr, SQLPOINTER value_ptr, - SQLINTEGER buffer_len, SQLINTEGER* str_len_ptr); -SQLRETURN SQLSetEnvAttr(SQLHENV env, SQLINTEGER attr, SQLPOINTER value_ptr, - SQLINTEGER str_len); -SQLRETURN SQLGetConnectAttr(SQLHDBC conn, SQLINTEGER attribute, SQLPOINTER value_ptr, - SQLINTEGER buffer_length, SQLINTEGER* string_length_ptr); -SQLRETURN SQLSetConnectAttr(SQLHDBC conn, SQLINTEGER attr, SQLPOINTER value, - SQLINTEGER value_len); -SQLRETURN SQLDriverConnect(SQLHDBC conn, SQLHWND window_handle, - SQLWCHAR* in_connection_string, - SQLSMALLINT in_connection_string_len, - SQLWCHAR* out_connection_string, - SQLSMALLINT out_connection_string_buffer_len, - SQLSMALLINT* out_connection_string_len, - SQLUSMALLINT driver_completion); -SQLRETURN SQLConnect(SQLHDBC conn, SQLWCHAR* dsn_name, SQLSMALLINT dsn_name_len, - SQLWCHAR* user_name, SQLSMALLINT user_name_len, SQLWCHAR* password, - SQLSMALLINT password_len); -SQLRETURN SQLDisconnect(SQLHDBC conn); -SQLRETURN SQLGetInfo(SQLHDBC conn, SQLUSMALLINT info_type, SQLPOINTER info_value_ptr, - SQLSMALLINT buf_len, SQLSMALLINT* length); -SQLRETURN SQLGetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, SQLPOINTER value_ptr, - SQLINTEGER buffer_length, SQLINTEGER* string_length_ptr); -SQLRETURN SQLSetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, SQLPOINTER value_ptr, - SQLINTEGER stringLength); -SQLRETURN SQLExecDirect(SQLHSTMT stmt, SQLWCHAR* queryText, SQLINTEGER text_length); -SQLRETURN SQLPrepare(SQLHSTMT stmt, SQLWCHAR* queryText, SQLINTEGER text_length); -SQLRETURN SQLExecute(SQLHSTMT stmt); -SQLRETURN SQLFetch(SQLHSTMT stmt); -SQLRETURN SQLExtendedFetch(SQLHSTMT stmt, SQLUSMALLINT fetch_orientation, - SQLLEN fetch_offset, SQLULEN* row_count_ptr, - SQLUSMALLINT* row_status_array); -SQLRETURN SQLFetchScroll(SQLHSTMT stmt, SQLSMALLINT fetch_orientation, - SQLLEN fetch_offset); -SQLRETURN SQLBindCol(SQLHSTMT stmt, SQLUSMALLINT record_number, SQLSMALLINT c_type, - SQLPOINTER data_ptr, SQLLEN buffer_length, SQLLEN* indicator_ptr); -SQLRETURN SQLCloseCursor(SQLHSTMT stmt); -SQLRETURN SQLGetData(SQLHSTMT stmt, SQLUSMALLINT record_number, SQLSMALLINT c_type, - SQLPOINTER data_ptr, SQLLEN buffer_length, SQLLEN* indicator_ptr); -SQLRETURN SQLMoreResults(SQLHSTMT stmt); -SQLRETURN SQLNumResultCols(SQLHSTMT stmt, SQLSMALLINT* column_count_ptr); -SQLRETURN SQLRowCount(SQLHSTMT stmt, SQLLEN* row_count_ptr); -SQLRETURN SQLTables(SQLHSTMT stmt, SQLWCHAR* catalog_name, - SQLSMALLINT catalog_name_length, SQLWCHAR* schema_name, - SQLSMALLINT schema_name_length, SQLWCHAR* table_name, - SQLSMALLINT table_name_length, SQLWCHAR* table_type, - SQLSMALLINT table_type_length); -SQLRETURN SQLColumns(SQLHSTMT stmt, SQLWCHAR* catalog_name, - SQLSMALLINT catalog_name_length, SQLWCHAR* schema_name, - SQLSMALLINT schema_name_length, SQLWCHAR* table_name, - SQLSMALLINT table_name_length, SQLWCHAR* column_name, - SQLSMALLINT column_name_length); -SQLRETURN SQLColAttribute(SQLHSTMT stmt, SQLUSMALLINT record_number, - SQLUSMALLINT field_identifier, - SQLPOINTER character_attribute_ptr, SQLSMALLINT buffer_length, - SQLSMALLINT* output_length, SQLLEN* numeric_attribute_ptr); -SQLRETURN SQLGetTypeInfo(SQLHSTMT stmt, SQLSMALLINT dataType); -SQLRETURN SQLNativeSql(SQLHDBC conn, SQLWCHAR* in_statement_text, - SQLINTEGER in_statement_text_length, SQLWCHAR* out_statement_text, - SQLINTEGER buffer_length, SQLINTEGER* out_statement_text_length); -SQLRETURN SQLDescribeCol(SQLHSTMT stmt, SQLUSMALLINT column_number, SQLWCHAR* column_name, - SQLSMALLINT buffer_length, SQLSMALLINT* name_length_ptr, - SQLSMALLINT* data_type_ptr, SQLULEN* column_size_ptr, - SQLSMALLINT* decimal_digits_ptr, SQLSMALLINT* nullable_ptr); +[[nodiscard]] SQLRETURN SQLAllocHandle(SQLSMALLINT type, SQLHANDLE parent, + SQLHANDLE* result); +[[nodiscard]] SQLRETURN SQLFreeHandle(SQLSMALLINT type, SQLHANDLE handle); +[[nodiscard]] SQLRETURN SQLFreeStmt(SQLHSTMT stmt, SQLUSMALLINT option); +[[nodiscard]] SQLRETURN SQLGetDiagField(SQLSMALLINT handle_type, SQLHANDLE handle, + SQLSMALLINT rec_number, + SQLSMALLINT diag_identifier, + SQLPOINTER diag_info_ptr, + SQLSMALLINT buffer_length, + SQLSMALLINT* string_length_ptr); +[[nodiscard]] SQLRETURN SQLGetDiagRec(SQLSMALLINT handle_type, SQLHANDLE handle, + SQLSMALLINT rec_number, SQLWCHAR* sql_state, + SQLINTEGER* native_error_ptr, + SQLWCHAR* message_text, SQLSMALLINT buffer_length, + SQLSMALLINT* text_length_ptr); +[[nodiscard]] SQLRETURN SQLGetEnvAttr(SQLHENV env, SQLINTEGER attr, SQLPOINTER value_ptr, + SQLINTEGER buffer_len, SQLINTEGER* str_len_ptr); +[[nodiscard]] SQLRETURN SQLSetEnvAttr(SQLHENV env, SQLINTEGER attr, SQLPOINTER value_ptr, + SQLINTEGER str_len); +[[nodiscard]] SQLRETURN SQLGetConnectAttr(SQLHDBC conn, SQLINTEGER attribute, + SQLPOINTER value_ptr, SQLINTEGER buffer_length, + SQLINTEGER* string_length_ptr); +[[nodiscard]] SQLRETURN SQLSetConnectAttr(SQLHDBC conn, SQLINTEGER attr, SQLPOINTER value, + SQLINTEGER value_len); +[[nodiscard]] SQLRETURN SQLDriverConnect(SQLHDBC conn, SQLHWND window_handle, + SQLWCHAR* in_connection_string, + SQLSMALLINT in_connection_string_len, + SQLWCHAR* out_connection_string, + SQLSMALLINT out_connection_string_buffer_len, + SQLSMALLINT* out_connection_string_len, + SQLUSMALLINT driver_completion); +[[nodiscard]] SQLRETURN SQLConnect(SQLHDBC conn, SQLWCHAR* dsn_name, + SQLSMALLINT dsn_name_len, SQLWCHAR* user_name, + SQLSMALLINT user_name_len, SQLWCHAR* password, + SQLSMALLINT password_len); +[[nodiscard]] SQLRETURN SQLDisconnect(SQLHDBC conn); +[[nodiscard]] SQLRETURN SQLGetInfo(SQLHDBC conn, SQLUSMALLINT info_type, + SQLPOINTER info_value_ptr, SQLSMALLINT buf_len, + SQLSMALLINT* length); +[[nodiscard]] SQLRETURN SQLGetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, + SQLPOINTER value_ptr, SQLINTEGER buffer_length, + SQLINTEGER* string_length_ptr); +[[nodiscard]] SQLRETURN SQLSetStmtAttr(SQLHSTMT stmt, SQLINTEGER attribute, + SQLPOINTER value_ptr, SQLINTEGER stringLength); +[[nodiscard]] SQLRETURN SQLExecDirect(SQLHSTMT stmt, SQLWCHAR* queryText, + SQLINTEGER text_length); +[[nodiscard]] SQLRETURN SQLPrepare(SQLHSTMT stmt, SQLWCHAR* queryText, + SQLINTEGER text_length); +[[nodiscard]] SQLRETURN SQLExecute(SQLHSTMT stmt); +[[nodiscard]] SQLRETURN SQLFetch(SQLHSTMT stmt); +[[nodiscard]] SQLRETURN SQLExtendedFetch(SQLHSTMT stmt, SQLUSMALLINT fetch_orientation, + SQLLEN fetch_offset, SQLULEN* row_count_ptr, + SQLUSMALLINT* row_status_array); +[[nodiscard]] SQLRETURN SQLFetchScroll(SQLHSTMT stmt, SQLSMALLINT fetch_orientation, + SQLLEN fetch_offset); +[[nodiscard]] SQLRETURN SQLBindCol(SQLHSTMT stmt, SQLUSMALLINT record_number, + SQLSMALLINT c_type, SQLPOINTER data_ptr, + SQLLEN buffer_length, SQLLEN* indicator_ptr); +[[nodiscard]] SQLRETURN SQLCloseCursor(SQLHSTMT stmt); +[[nodiscard]] SQLRETURN SQLGetData(SQLHSTMT stmt, SQLUSMALLINT record_number, + SQLSMALLINT c_type, SQLPOINTER data_ptr, + SQLLEN buffer_length, SQLLEN* indicator_ptr); +[[nodiscard]] SQLRETURN SQLMoreResults(SQLHSTMT stmt); +[[nodiscard]] SQLRETURN SQLNumResultCols(SQLHSTMT stmt, SQLSMALLINT* column_count_ptr); +[[nodiscard]] SQLRETURN SQLRowCount(SQLHSTMT stmt, SQLLEN* row_count_ptr); +[[nodiscard]] SQLRETURN SQLTables(SQLHSTMT stmt, SQLWCHAR* catalog_name, + SQLSMALLINT catalog_name_length, SQLWCHAR* schema_name, + SQLSMALLINT schema_name_length, SQLWCHAR* table_name, + SQLSMALLINT table_name_length, SQLWCHAR* table_type, + SQLSMALLINT table_type_length); +[[nodiscard]] SQLRETURN SQLColumns(SQLHSTMT stmt, SQLWCHAR* catalog_name, + SQLSMALLINT catalog_name_length, SQLWCHAR* schema_name, + SQLSMALLINT schema_name_length, SQLWCHAR* table_name, + SQLSMALLINT table_name_length, SQLWCHAR* column_name, + SQLSMALLINT column_name_length); +[[nodiscard]] SQLRETURN SQLColAttribute(SQLHSTMT stmt, SQLUSMALLINT record_number, + SQLUSMALLINT field_identifier, + SQLPOINTER character_attribute_ptr, + SQLSMALLINT buffer_length, + SQLSMALLINT* output_length, + SQLLEN* numeric_attribute_ptr); +[[nodiscard]] SQLRETURN SQLGetTypeInfo(SQLHSTMT stmt, SQLSMALLINT dataType); +[[nodiscard]] SQLRETURN SQLNativeSql(SQLHDBC conn, SQLWCHAR* in_statement_text, + SQLINTEGER in_statement_text_length, + SQLWCHAR* out_statement_text, + SQLINTEGER buffer_length, + SQLINTEGER* out_statement_text_length); +[[nodiscard]] SQLRETURN SQLDescribeCol( + SQLHSTMT stmt, SQLUSMALLINT column_number, SQLWCHAR* column_name, + SQLSMALLINT buffer_length, SQLSMALLINT* name_length_ptr, SQLSMALLINT* data_type_ptr, + SQLULEN* column_size_ptr, SQLSMALLINT* decimal_digits_ptr, SQLSMALLINT* nullable_ptr); } // namespace arrow::flight::sql::odbc