From 76e628c9632e5f0195d2b00435899cfc329c3f61 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Thu, 28 Sep 2017 13:22:00 -0700 Subject: [PATCH] resumed error handling when driver option is set and added test cases --- source/pdo_sqlsrv/pdo_util.cpp | 4 + source/shared/core_conn.cpp | 81 ++++++++++++++----- source/shared/core_sqlsrv.h | 1 + source/sqlsrv/util.cpp | 4 + .../pdo_sqlsrv/pdo_connect_driver.phpt | 51 +++++++++--- .../sqlsrv/sqlsrv_connect_driver.phpt | 40 +++++++-- 6 files changed, 143 insertions(+), 38 deletions(-) diff --git a/source/pdo_sqlsrv/pdo_util.cpp b/source/pdo_sqlsrv/pdo_util.cpp index 2c0d43e4..63cad9d2 100644 --- a/source/pdo_sqlsrv/pdo_util.cpp +++ b/source/pdo_sqlsrv/pdo_util.cpp @@ -405,6 +405,10 @@ pdo_error PDO_ERRORS[] = { SQLSRV_ERROR_CONNECT_INVALID_DRIVER, { IMSSP, (SQLCHAR*) "Invalid value %1!s! was specified for Driver option.", -79, true } }, + { + SQLSRV_ERROR_SPECIFIED_DRIVER_NOT_FOUND, + { IMSSP, (SQLCHAR*) "The specified ODBC Driver is not found.", -80, false } + }, { UINT_MAX, {} } }; diff --git a/source/shared/core_conn.cpp b/source/shared/core_conn.cpp index defef8f2..70450a12 100644 --- a/source/shared/core_conn.cpp +++ b/source/shared/core_conn.cpp @@ -150,6 +150,7 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont build_connection_string_and_set_conn_attr( conn, server, uid, pwd, options_ht, valid_conn_opts, driver, conn_str TSRMLS_CC ); +#ifndef _WIN32 if( conn->ce_option.enabled ) { // when AE is enabled, must use ODBC driver 17 if( conn->driver_version != ODBC_DRIVER_UNKNOWN ) { @@ -157,15 +158,19 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont throw core::CoreException(); } + // check if ODBC 17 actually exists, if not, throw an exception + CHECK_CUSTOM_ERROR( ! core_search_odbc_driver_unix( ODBC_DRIVER_17 ), conn, SQLSRV_ERROR_SPECIFIED_DRIVER_NOT_FOUND ) { + throw core::CoreException(); + } + r = core_odbc_connect( conn, conn_str, is_pooled ); } else { // driver not specified, so connect using ODBC 17 -#ifndef _WIN32 // In non-Windows environment, unixODBC 2.3.4 and unixODBC 2.3.1 return different error states // If it fails to connect using ODBC 17, it is unreliable to check for a certain sql state error. // Thus, we will simply throw an exception. - if( core_search_odbc_driver_unix( DRIVER_VERSION::ODBC_DRIVER_17 ) ) { + if( core_search_odbc_driver_unix( ODBC_DRIVER_17 ) ) { conn_str = conn_str + CONNECTION_STRING_DRIVER_NAME[ DRIVER_VERSION::ODBC_DRIVER_17 ]; r = core_odbc_connect( conn, conn_str, is_pooled ); } @@ -174,27 +179,21 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont throw core::CoreException(); } } -#else - conn_str = conn_str + CONNECTION_STRING_DRIVER_NAME[ DRIVER_VERSION::ODBC_DRIVER_17 ]; - r = core_odbc_connect( conn, conn_str, is_pooled ); - - if(! SQL_SUCCEEDED( r ) ) { - // sql state IM002 means that the specified ODBC driver is not installed - CHECK_CUSTOM_ERROR( core_compare_error_state( conn, r, "IM002" ) , conn, SQLSRV_ERROR_AE_DRIVER_REQUIRED, get_processor_arch() ) { - throw core::CoreException(); - } - } -#endif // !_WIN32 - } + } // else driver_version not unknown } else { + // column encryption NOT enabled if( conn->driver_version != ODBC_DRIVER_UNKNOWN ) { + // check if the ODBC driver actually exists, if not, throw an exception + CHECK_CUSTOM_ERROR( ! core_search_odbc_driver_unix( conn->driver_version ), conn, SQLSRV_ERROR_SPECIFIED_DRIVER_NOT_FOUND ) { + throw core::CoreException(); + } + r = core_odbc_connect( conn, conn_str, is_pooled ); } else { -#ifndef _WIN32 DRIVER_VERSION odbc_version = ODBC_DRIVER_UNKNOWN; - for ( short i = DRIVER_VERSION::FIRST; i <= DRIVER_VERSION::LAST; ++i ) { + for( short i = DRIVER_VERSION::FIRST; i <= DRIVER_VERSION::LAST; ++i ) { // skip ODBC 11 in a non-Windows environment -- only available in Red Hat / SUSE (preview) // https://docs.microsoft.com/en-us/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server#microsoft-odbc-driver-11-for-sql-server-on-linux if (i == DRIVER_VERSION::ODBC_DRIVER_11) @@ -210,8 +209,52 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont } std::string conn_str_driver = conn_str + CONNECTION_STRING_DRIVER_NAME[ DRIVER_VERSION( odbc_version ) ]; r = core_odbc_connect( conn, conn_str_driver, is_pooled ); + } // else driver_version not unknown + } // else ce_option enabled #else - for ( short i = DRIVER_VERSION::FIRST; i <= DRIVER_VERSION::LAST; ++i ) { + if( conn->ce_option.enabled ) { + // when AE is enabled, must use ODBC driver 17 + if( conn->driver_version != ODBC_DRIVER_UNKNOWN ) { + CHECK_CUSTOM_ERROR( conn->driver_version != ODBC_DRIVER_17, conn, SQLSRV_ERROR_AE_DRIVER_REQUIRED, get_processor_arch() ) { + throw core::CoreException(); + } + + r = core_odbc_connect( conn, conn_str, is_pooled ); + + if( ! SQL_SUCCEEDED( r ) && core_compare_error_state( conn, r, "IM002" ) ) { + // sql state IM002 means that the specified ODBC driver is not installed + CHECK_CUSTOM_ERROR( true, conn, SQLSRV_ERROR_SPECIFIED_DRIVER_NOT_FOUND ) { + throw core::CoreException(); + } + } + } + else { + // driver not specified, so connect using ODBC 17 + conn_str = conn_str + CONNECTION_STRING_DRIVER_NAME[ DRIVER_VERSION::ODBC_DRIVER_17 ]; + r = core_odbc_connect( conn, conn_str, is_pooled ); + + if(! SQL_SUCCEEDED( r ) ) { + // sql state IM002 means that the specified ODBC driver is not installed + CHECK_CUSTOM_ERROR( core_compare_error_state( conn, r, "IM002" ) , conn, SQLSRV_ERROR_AE_DRIVER_REQUIRED, get_processor_arch() ) { + throw core::CoreException(); + } + } + } + } + else { + // column encryption NOT enabled + if( conn->driver_version != ODBC_DRIVER_UNKNOWN ) { + r = core_odbc_connect( conn, conn_str, is_pooled ); + + if( ! SQL_SUCCEEDED( r ) && core_compare_error_state( conn, r, "IM002" ) ) { + // sql state IM002 means that the specified ODBC driver is not installed + CHECK_CUSTOM_ERROR( true, conn, SQLSRV_ERROR_SPECIFIED_DRIVER_NOT_FOUND, CONNECTION_STRING_DRIVER_NAME[ conn->driver_version ].c_str() ) { + throw core::CoreException(); + } + } + } + else { + for( short i = DRIVER_VERSION::FIRST; i <= DRIVER_VERSION::LAST; ++i ) { std::string conn_str_driver = conn_str + CONNECTION_STRING_DRIVER_NAME[ DRIVER_VERSION(i) ]; r = core_odbc_connect( conn, conn_str_driver, is_pooled ); @@ -231,10 +274,10 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont } } } // for -#endif // !_WIN32 } } // else ce_option enabled - +#endif // !_WIN32 + CHECK_SQL_ERROR( r, conn ) { throw core::CoreException(); } diff --git a/source/shared/core_sqlsrv.h b/source/shared/core_sqlsrv.h index 1b6dda41..7e8c79c4 100644 --- a/source/shared/core_sqlsrv.h +++ b/source/shared/core_sqlsrv.h @@ -1644,6 +1644,7 @@ enum SQLSRV_ERROR_CODES { SQLSRV_ERROR_DRIVER_NOT_INSTALLED, SQLSRV_ERROR_AE_DRIVER_REQUIRED, SQLSRV_ERROR_CONNECT_INVALID_DRIVER, + SQLSRV_ERROR_SPECIFIED_DRIVER_NOT_FOUND, SQLSRV_ERROR_ZEND_HASH, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, SQLSRV_ERROR_INVALID_PARAMETER_SQLTYPE, diff --git a/source/sqlsrv/util.cpp b/source/sqlsrv/util.cpp index 0e527d0c..d282c9c0 100644 --- a/source/sqlsrv/util.cpp +++ b/source/sqlsrv/util.cpp @@ -404,6 +404,10 @@ ss_error SS_ERRORS[] = { SQLSRV_ERROR_CONNECT_INVALID_DRIVER, { IMSSP, (SQLCHAR*) "Invalid value %1!s! was specified for Driver option.", -106, true } }, + { + SQLSRV_ERROR_SPECIFIED_DRIVER_NOT_FOUND, + { IMSSP, (SQLCHAR*) "The specified ODBC Driver is not found.", -107, false } + }, // terminate the list of errors/warnings { UINT_MAX, {} } }; diff --git a/test/functional/pdo_sqlsrv/pdo_connect_driver.phpt b/test/functional/pdo_sqlsrv/pdo_connect_driver.phpt index 72a72fd4..1ab37920 100644 --- a/test/functional/pdo_sqlsrv/pdo_connect_driver.phpt +++ b/test/functional/pdo_sqlsrv/pdo_connect_driver.phpt @@ -124,36 +124,61 @@ function test_invalid_values() function test_encrypted_with_odbc() { - global $msodbcsql_maj; - + global $msodbcsql_maj, $server, $uid, $pwd; + $value = "ODBC Driver 13 for SQL Server"; $connectionOptions = "Driver = $value; ColumnEncryption = Enabled;"; $expected = "The Always Encrypted feature requires Microsoft ODBC Driver 17 for SQL Server."; connect_verify_output( $connectionOptions, $expected ); + + // TODO: the following block will change once ODBC 17 is officially released + $value = "ODBC Driver 17 for SQL Server"; + $connectionOptions = "Driver = $value; ColumnEncryption = Enabled;"; + + $success = "Successfully connected with column encryption."; + $expected = "The specified ODBC Driver is not found."; + $message = $success; + try + { + $conn = new PDO( "sqlsrv:server = $server ; $connectionOptions", $uid, $pwd ); + } + catch( PDOException $e ) + { + $message = $e->getMessage(); + } + + if ( $msodbcsql_maj == 17 ) + { + // this indicates that OCBC 17 is the only available driver + if ( strcmp( $message, $success ) ) + print_r( $message ); + } + else + { + // OCBC 17 might or might not exist + if ( strcmp( $message, $success ) ) + { + if ( strpos( $message, $expected ) === false ) + print_r( $message ); + } + } } function test_wrong_odbc() { - global $msodbcsql_maj, $server, $uid, $pwd; + global $msodbcsql_maj; + // TODO: this will change once ODBC 17 is officially released $value = "ODBC Driver 17 for SQL Server"; if ( $msodbcsql_maj == 17 || $msodbcsql_maj < 13 ) { $value = "ODBC Driver 13 for SQL Server"; } $connectionOptions = "Driver = $value;"; + $expected = "The specified ODBC Driver is not found."; - try - { - $conn = new PDO( "sqlsrv:server = $server ; $connectionOptions", $uid, $pwd ); - - echo "Should have caused an exception connecting with $value!\n"; - } - catch( PDOException $e ) - { - // do nothing here because this is expected - } + connect_verify_output( $connectionOptions, $expected ); } ?> diff --git a/test/functional/sqlsrv/sqlsrv_connect_driver.phpt b/test/functional/sqlsrv/sqlsrv_connect_driver.phpt index cd6a6ada..27032b07 100644 --- a/test/functional/sqlsrv/sqlsrv_connect_driver.phpt +++ b/test/functional/sqlsrv/sqlsrv_connect_driver.phpt @@ -119,23 +119,51 @@ function test_encrypted_with_odbc( $msodbcsql_maj, $server, $connectionOptions ) $expected = "The Always Encrypted feature requires Microsoft ODBC Driver 17 for SQL Server."; connect_verify_output( $server, $connectionOptions, $expected ); + + // TODO: the following block will change once ODBC 17 is officially released + $value = "ODBC Driver 17 for SQL Server"; + $connectionOptions['Driver']=$value; + $connectionOptions['ColumnEncryption']='Enabled'; + + $success = "Successfully connected with column encryption."; + $expected = "The specified ODBC Driver is not found."; + $message = $success; + + $conn = sqlsrv_connect($server, $connectionOptions); + if ($conn === false) + $message = sqlsrv_errors($conn)[0]['message']; + + if ( $msodbcsql_maj == 17 ) + { + // this indicates that OCBC 17 is the only available driver + if ( strcmp( $message, $success ) ) + print_r( $message ); + } + else + { + // OCBC 17 might or might not exist + if ( strcmp( $message, $success ) ) + { + if ( strpos( $message, $expected ) === false ) + print_r( $message ); + } + } + } function test_wrong_odbc( $msodbcsql_maj, $server, $connectionOptions ) { + // TODO: this will change once ODBC 17 is officially released $value = "ODBC Driver 17 for SQL Server"; if ( $msodbcsql_maj == 17 || $msodbcsql_maj < 13 ) { $value = "ODBC Driver 13 for SQL Server"; } - $connectionOptions['Driver']=$value; + $connectionOptions['Driver']=$value; + $expected = "The specified ODBC Driver is not found."; - $conn = sqlsrv_connect($server, $connectionOptions); - if ($conn != false) - { - echo "Expected errors connecting with $value!\n"; - } + connect_verify_output( $server, $connectionOptions, $expected ); } ?>