3131#include " arrow/flight/sql/odbc/odbc_impl/spi/connection.h"
3232#include " arrow/util/logging.h"
3333
34+ #if defined _WIN32
35+ // For displaying DSN Window
36+ # include " arrow/flight/sql/odbc/odbc_impl/system_dsn.h"
37+ #endif // defined(_WIN32)
38+
3439namespace arrow ::flight::sql::odbc {
3540SQLRETURN SQLAllocHandle (SQLSMALLINT type, SQLHANDLE parent, SQLHANDLE* result) {
3641 ARROW_LOG (DEBUG) << " SQLAllocHandle called with type: " << type
@@ -718,8 +723,30 @@ SQLRETURN SQLSetConnectAttr(SQLHDBC conn, SQLINTEGER attr, SQLPOINTER value_ptr,
718723 ARROW_LOG (DEBUG) << " SQLSetConnectAttrW called with conn: " << conn
719724 << " , attr: " << attr << " , value_ptr: " << value_ptr
720725 << " , value_len: " << value_len;
721- // GH-47708 TODO: Implement SQLSetConnectAttr
722- return SQL_INVALID_HANDLE;
726+ // GH-47708 TODO: Add tests for SQLSetConnectAttr
727+ using ODBC::ODBCConnection;
728+
729+ return ODBCConnection::ExecuteWithDiagnostics (conn, SQL_ERROR, [=]() {
730+ const bool is_unicode = true ;
731+ ODBCConnection* connection = reinterpret_cast <ODBCConnection*>(conn);
732+ connection->SetConnectAttr (attr, value_ptr, value_len, is_unicode);
733+ return SQL_SUCCESS;
734+ });
735+ }
736+
737+ // Load properties from the given DSN. The properties loaded do _not_ overwrite existing
738+ // entries in the properties.
739+ void LoadPropertiesFromDSN (const std::string& dsn,
740+ Connection::ConnPropertyMap& properties) {
741+ arrow::flight::sql::odbc::config::Configuration config;
742+ config.LoadDsn (dsn);
743+ Connection::ConnPropertyMap dsn_properties = config.GetProperties ();
744+ for (auto & [key, value] : dsn_properties) {
745+ auto prop_iter = properties.find (key);
746+ if (prop_iter == properties.end ()) {
747+ properties.emplace (std::make_pair (std::move (key), std::move (value)));
748+ }
749+ }
723750}
724751
725752SQLRETURN SQLDriverConnect (SQLHDBC conn, SQLHWND window_handle,
@@ -740,13 +767,73 @@ SQLRETURN SQLDriverConnect(SQLHDBC conn, SQLHWND window_handle,
740767 << out_connection_string_buffer_len << " , out_connection_string_len: "
741768 << static_cast <const void *>(out_connection_string_len)
742769 << " , driver_completion: " << driver_completion;
770+
743771 // GH-46449 TODO: Implement FILEDSN and SAVEFILE keywords according to the spec
744772
745773 // GH-46560 TODO: Copy connection string properly in SQLDriverConnect according to the
746774 // spec
747775
748- // GH-46574 TODO: Implement SQLDriverConnect
749- return SQL_INVALID_HANDLE;
776+ using ODBC::ODBCConnection;
777+
778+ return ODBCConnection::ExecuteWithDiagnostics (conn, SQL_ERROR, [=]() {
779+ ODBCConnection* connection = reinterpret_cast <ODBCConnection*>(conn);
780+ std::string connection_string =
781+ ODBC::SqlWcharToString (in_connection_string, in_connection_string_len);
782+ Connection::ConnPropertyMap properties;
783+ std::string dsn_value = " " ;
784+ std::optional<std::string> dsn = ODBCConnection::GetDsnIfExists (connection_string);
785+ if (dsn.has_value ()) {
786+ dsn_value = dsn.value ();
787+ LoadPropertiesFromDSN (dsn_value, properties);
788+ }
789+ ODBCConnection::GetPropertiesFromConnString (connection_string, properties);
790+
791+ std::vector<std::string_view> missing_properties;
792+
793+ // GH-46448 TODO: Implement SQL_DRIVER_COMPLETE_REQUIRED in SQLDriverConnect according
794+ // to the spec
795+ #if defined _WIN32
796+ // Load the DSN window according to driver_completion
797+ if (driver_completion == SQL_DRIVER_PROMPT) {
798+ // Load DSN window before first attempt to connect
799+ arrow::flight::sql::odbc::config::Configuration config;
800+ if (!DisplayConnectionWindow (window_handle, config, properties)) {
801+ return static_cast <SQLRETURN>(SQL_NO_DATA);
802+ }
803+ connection->Connect (dsn_value, properties, missing_properties);
804+ } else if (driver_completion == SQL_DRIVER_COMPLETE ||
805+ driver_completion == SQL_DRIVER_COMPLETE_REQUIRED) {
806+ try {
807+ connection->Connect (dsn_value, properties, missing_properties);
808+ } catch (const DriverException&) {
809+ // If first connection fails due to missing attributes, load
810+ // the DSN window and try to connect again
811+ if (!missing_properties.empty ()) {
812+ arrow::flight::sql::odbc::config::Configuration config;
813+ missing_properties.clear ();
814+
815+ if (!DisplayConnectionWindow (window_handle, config, properties)) {
816+ return static_cast <SQLRETURN>(SQL_NO_DATA);
817+ }
818+ connection->Connect (dsn_value, properties, missing_properties);
819+ } else {
820+ throw ;
821+ }
822+ }
823+ } else {
824+ // Default case: attempt connection without showing DSN window
825+ connection->Connect (dsn_value, properties, missing_properties);
826+ }
827+ #else
828+ // Attempt connection without loading DSN window on macOS/Linux
829+ connection->Connect (dsn, properties, missing_properties);
830+ #endif
831+ // Copy connection string to out_connection_string after connection attempt
832+ return ODBC::GetStringAttribute (true , connection_string, false , out_connection_string,
833+ out_connection_string_buffer_len,
834+ out_connection_string_len,
835+ connection->GetDiagnostics ());
836+ });
750837}
751838
752839SQLRETURN SQLConnect (SQLHDBC conn, SQLWCHAR* dsn_name, SQLSMALLINT dsn_name_len,
@@ -759,14 +846,48 @@ SQLRETURN SQLConnect(SQLHDBC conn, SQLWCHAR* dsn_name, SQLSMALLINT dsn_name_len,
759846 << " , user_name_len: " << user_name_len
760847 << " , password: " << static_cast <const void *>(password)
761848 << " , password_len: " << password_len;
762- // GH-46574 TODO: Implement SQLConnect
763- return SQL_INVALID_HANDLE;
849+
850+ using ODBC::ODBCConnection;
851+
852+ using ODBC::SqlWcharToString;
853+
854+ return ODBCConnection::ExecuteWithDiagnostics (conn, SQL_ERROR, [=]() {
855+ ODBCConnection* connection = reinterpret_cast <ODBCConnection*>(conn);
856+ std::string dsn = SqlWcharToString (dsn_name, dsn_name_len);
857+
858+ Configuration config;
859+ config.LoadDsn (dsn);
860+
861+ if (user_name) {
862+ std::string uid = SqlWcharToString (user_name, user_name_len);
863+ config.Emplace (FlightSqlConnection::UID, std::move (uid));
864+ }
865+
866+ if (password) {
867+ std::string pwd = SqlWcharToString (password, password_len);
868+ config.Emplace (FlightSqlConnection::PWD, std::move (pwd));
869+ }
870+
871+ std::vector<std::string_view> missing_properties;
872+
873+ connection->Connect (dsn, config.GetProperties (), missing_properties);
874+
875+ return SQL_SUCCESS;
876+ });
764877}
765878
766879SQLRETURN SQLDisconnect (SQLHDBC conn) {
767880 ARROW_LOG (DEBUG) << " SQLDisconnect called with conn: " << conn;
768- // GH-46574 TODO: Implement SQLDisconnect
769- return SQL_INVALID_HANDLE;
881+
882+ using ODBC::ODBCConnection;
883+
884+ return ODBCConnection::ExecuteWithDiagnostics (conn, SQL_ERROR, [=]() {
885+ ODBCConnection* connection = reinterpret_cast <ODBCConnection*>(conn);
886+
887+ connection->Disconnect ();
888+
889+ return SQL_SUCCESS;
890+ });
770891}
771892
772893SQLRETURN SQLGetInfo (SQLHDBC conn, SQLUSMALLINT info_type, SQLPOINTER info_value_ptr,
@@ -776,8 +897,24 @@ SQLRETURN SQLGetInfo(SQLHDBC conn, SQLUSMALLINT info_type, SQLPOINTER info_value
776897 << " , info_value_ptr: " << info_value_ptr << " , buf_len: " << buf_len
777898 << " , string_length_ptr: "
778899 << static_cast <const void *>(string_length_ptr);
779- // GH-47709 TODO: Implement SQLGetInfo
780- return SQL_INVALID_HANDLE;
900+
901+ // GH-47709 TODO: Update SQLGetInfo implementation and add tests for SQLGetInfo
902+ using ODBC::ODBCConnection;
903+
904+ return ODBCConnection::ExecuteWithDiagnostics (conn, SQL_ERROR, [=]() {
905+ ODBCConnection* connection = reinterpret_cast <ODBCConnection*>(conn);
906+
907+ // Set character type to be Unicode by default
908+ const bool is_unicode = true ;
909+
910+ if (!info_value_ptr && !string_length_ptr) {
911+ return static_cast <SQLRETURN>(SQL_ERROR);
912+ }
913+
914+ connection->GetInfo (info_type, info_value_ptr, buf_len, string_length_ptr,
915+ is_unicode);
916+ return static_cast <SQLRETURN>(SQL_SUCCESS);
917+ });
781918}
782919
783920SQLRETURN SQLGetStmtAttr (SQLHSTMT stmt, SQLINTEGER attribute, SQLPOINTER value_ptr,
0 commit comments