diff --git a/source/shared/core_conn.cpp b/source/shared/core_conn.cpp index b157a797..b573e72a 100644 --- a/source/shared/core_conn.cpp +++ b/source/shared/core_conn.cpp @@ -48,9 +48,8 @@ const int INFO_BUFFER_LEN = 256; // length for name of keystore used in CEKeyStoreData const int MAX_CE_NAME_LEN = 260; -// ODBC driver names. -// the order of this list should match the order of DRIVER_VERSION enum -std::vector CONNECTION_STRING_DRIVER_NAME{ "Driver={ODBC Driver 17 for SQL Server};", "Driver={ODBC Driver 13 for SQL Server};", "Driver={ODBC Driver 11 for SQL Server};" }; +// ODBC driver name +const char ODBC_DRIVER_NAME[] = "ODBC Driver %d for SQL Server"; // default options if only the server is specified const char CONNECTION_STRING_DEFAULT_OPTIONS[] = "Mars_Connection={Yes};"; @@ -73,6 +72,11 @@ void common_conn_str_append_func( _In_z_ const char* odbc_name, _In_reads_(val_l void load_azure_key_vault( _Inout_ sqlsrv_conn* conn ); void configure_azure_key_vault( sqlsrv_conn* conn, BYTE config_attr, const DWORD config_value, size_t key_size); void configure_azure_key_vault( sqlsrv_conn* conn, BYTE config_attr, const char* config_value, size_t key_size); +std::string get_ODBC_driver_name(_In_ ODBC_DRIVER driver); +#ifndef _WIN32 +bool core_search_odbc_driver_unix(_In_ ODBC_DRIVER driver); +#endif + } // core_sqlsrv_connect @@ -151,93 +155,70 @@ 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 ); - // If column encryption is enabled, must use ODBC driver 17 - if( conn->ce_option.enabled && conn->driver_version != ODBC_DRIVER_UNKNOWN) { - CHECK_CUSTOM_ERROR( conn->driver_version != ODBC_DRIVER_17, conn, SQLSRV_ERROR_CE_DRIVER_REQUIRED, get_processor_arch() ) { - throw core::CoreException(); - } - } - // In non-Windows environment, unixODBC 2.3.4 and unixODBC 2.3.1 return different error states when an ODBC driver exists or not // Therefore, it is unreliable to check for a certain sql state error + // In Windows, we try to connect with ODBC driver first and rely on the returned error code to try connecting with other supported ODBC drivers + if (conn->driver_version != ODBC_DRIVER::VER_UNKNOWN) { + // if column encryption is enabled, must use ODBC driver 17 or above + CHECK_CUSTOM_ERROR(conn->ce_option.enabled && conn->driver_version == ODBC_DRIVER::VER_13, conn, SQLSRV_ERROR_CE_DRIVER_REQUIRED, get_processor_arch()) { + throw core::CoreException(); + } + #ifndef _WIN32 - 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 ) { + 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 { - if( conn->ce_option.enabled ) { - // driver not specified, so check if ODBC 17 exists - CHECK_CUSTOM_ERROR( ! core_search_odbc_driver_unix( ODBC_DRIVER_17 ), conn, SQLSRV_ERROR_CE_DRIVER_REQUIRED, get_processor_arch()) { - throw core::CoreException(); - } - - conn_str = conn_str + CONNECTION_STRING_DRIVER_NAME[ODBC_DRIVER_17]; - r = core_odbc_connect( conn, conn_str, is_pooled ); - } - else { - // 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 - - DRIVER_VERSION odbc_version = ODBC_DRIVER_UNKNOWN; - if( core_search_odbc_driver_unix( ODBC_DRIVER_17 ) ) { - odbc_version = ODBC_DRIVER_17; - } - else if ( core_search_odbc_driver_unix( ODBC_DRIVER_13 ) ) { - odbc_version = ODBC_DRIVER_13; - } - - CHECK_CUSTOM_ERROR( odbc_version == ODBC_DRIVER_UNKNOWN, conn, SQLSRV_ERROR_DRIVER_NOT_INSTALLED, get_processor_arch() ) { - throw core::CoreException(); - } - std::string conn_str_driver = conn_str + CONNECTION_STRING_DRIVER_NAME[odbc_version]; - r = core_odbc_connect( conn, conn_str_driver, is_pooled ); - } // else ce_option enabled - } // else driver_version not unknown + // if the driver exists, connect + r = core_odbc_connect(conn, conn_str, is_pooled); #else - if( conn->driver_version != ODBC_DRIVER_UNKNOWN ) { - r = core_odbc_connect( conn, conn_str, is_pooled ); + // try to connect with the specified ODBC driver + r = core_odbc_connect(conn, conn_str, is_pooled); - // check if the specified ODBC driver is there - CHECK_CUSTOM_ERROR( core_compare_error_state( conn, r, "IM002" ), conn, SQLSRV_ERROR_SPECIFIED_DRIVER_NOT_FOUND ) { + // if the specified ODBC driver does not exist, the error code is "IM002" (i.e. Data source name not found) + CHECK_CUSTOM_ERROR(core_compare_error_state(conn, r, "IM002"), conn, SQLSRV_ERROR_SPECIFIED_DRIVER_NOT_FOUND) { throw core::CoreException(); } +#endif } else { - if( conn->ce_option.enabled ) { - // driver not specified, so connect using ODBC 17 - conn_str = conn_str + CONNECTION_STRING_DRIVER_NAME[ODBC_DRIVER_17]; - r = core_odbc_connect( conn, conn_str, is_pooled ); + // ODBC driver not specified, so check ODBC 17 first then ODBC 18 and/or ODBC 13 + // If column encryption is enabled, check up to ODBC 18 + ODBC_DRIVER drivers[] = { ODBC_DRIVER::VER_17, ODBC_DRIVER::VER_18, ODBC_DRIVER::VER_13 }; + ODBC_DRIVER last_version = (conn->ce_option.enabled) ? ODBC_DRIVER::VER_18 : ODBC_DRIVER::VER_13; + + ODBC_DRIVER version = ODBC_DRIVER::VER_UNKNOWN; + for (auto &d : drivers) { + std::string driver_name = get_ODBC_driver_name(d); +#ifndef _WIN32 + if (core_search_odbc_driver_unix(d)) { + // now append the driver name to the connection string + common_conn_str_append_func(ODBCConnOptions::Driver, driver_name.c_str(), driver_name.length(), conn_str); + r = core_odbc_connect(conn, conn_str, is_pooled); + break; + } +#else + std::string conn_str_driver = conn_str; // use a copy of conn_str instead + common_conn_str_append_func(ODBCConnOptions::Driver, driver_name.c_str(), driver_name.length(), conn_str_driver); + r = core_odbc_connect(conn, conn_str_driver, is_pooled); + if (SQL_SUCCEEDED(r) || !core_compare_error_state(conn, r, "IM002")) { + // something else went wrong, exit the loop now other than ODBC driver not found + break; + } +#endif + else if (d == last_version) { + // if column encryption is enabled, throw the exception related to column encryption + CHECK_CUSTOM_ERROR(conn->ce_option.enabled, conn, SQLSRV_ERROR_CE_DRIVER_REQUIRED, get_processor_arch()) { + throw core::CoreException(); + } - // check if the specified ODBC driver is there - CHECK_CUSTOM_ERROR( core_compare_error_state( conn, r, "IM002" ) , conn, SQLSRV_ERROR_CE_DRIVER_REQUIRED, get_processor_arch() ) { - throw core::CoreException(); - } + // here it means that none of the supported ODBC drivers is found + CHECK_CUSTOM_ERROR(true, conn, SQLSRV_ERROR_DRIVER_NOT_INSTALLED, get_processor_arch()) { + throw core::CoreException(); + } + } } - else { - bool done = false; - for( short i = DRIVER_VERSION::FIRST; i <= DRIVER_VERSION::LAST && ! done; ++i ) { - std::string conn_str_driver = conn_str + CONNECTION_STRING_DRIVER_NAME[i]; - r = core_odbc_connect( conn, conn_str_driver, is_pooled ); - - if( SQL_SUCCEEDED( r ) || ! core_compare_error_state( conn, r, "IM002" ) ) { - // something else went wrong, exit the loop now other than ODBC driver not found - done = true; - } - else { - // did it fail to find the last valid ODBC driver? - CHECK_CUSTOM_ERROR( ( i == DRIVER_VERSION::LAST ), conn, SQLSRV_ERROR_DRIVER_NOT_INSTALLED, get_processor_arch()) { - throw core::CoreException(); - } - } - } // for - } // else ce_option enabled - } // else driver_version not unknown -#endif // !_WIN32 + } // time to free the access token, if not null if (conn->azure_ad_access_token) { @@ -322,50 +303,6 @@ bool core_compare_error_state( _In_ sqlsrv_conn* conn, _In_ SQLRETURN rc, _In_ return ( SQL_SUCCEEDED(sr) && ! strcmp(error_state, reinterpret_cast( state ) ) ); } -// core_search_odbc_driver_unix -// This method is meant to be used in a non-Windows environment, -// searching for a particular ODBC driver name in the odbcinst.ini file -// Parameters: -// driver_version - a valid value in enum DRIVER_VERSION -// Return - a boolean flag that indicates if the specified driver version is found or not - -bool core_search_odbc_driver_unix( _In_ DRIVER_VERSION driver_version ) -{ -#ifndef _WIN32 - char szBuf[DEFAULT_CONN_STR_LEN+1] = {'\0'}; // use a large enough buffer size - WORD cbBufMax = DEFAULT_CONN_STR_LEN; - WORD cbBufOut; - char *pszBuf = szBuf; - - // get all the names of the installed drivers delimited by null characters - if(! SQLGetInstalledDrivers( szBuf, cbBufMax, &cbBufOut ) ) - { - return false; - } - - // extract the ODBC driver name - std::string driver = CONNECTION_STRING_DRIVER_NAME[driver_version]; - std::size_t pos1 = driver.find_first_of("{"); - std::size_t pos2 = driver.find_first_of("}"); - std::string driver_str = driver.substr( pos1 + 1, pos2 - pos1 - 1); - - // search for the ODBC driver... - const char* driver_name = driver_str.c_str(); - do - { - if( strstr( pszBuf, driver_name ) != 0 ) - { - return true; - } - // get the next driver - pszBuf = strchr( pszBuf, '\0' ) + 1; - } - while( pszBuf[1] != '\0' ); // end when there are two consecutive null characters -#endif // !_WIN32 - - return false; -} - // core_odbc_connect // calls odbc connect API to establish the connection to server // Parameters: @@ -1010,6 +947,48 @@ void common_conn_str_append_func( _In_z_ const char* odbc_name, _In_reads_(val_l conn_str += "};"; } +std::string get_ODBC_driver_name(_In_ ODBC_DRIVER driver) +{ + const short BUFFER_LEN = sizeof(ODBC_DRIVER_NAME); + char driver_name[BUFFER_LEN] = { '\0' }; + snprintf(driver_name, BUFFER_LEN, ODBC_DRIVER_NAME, static_cast(driver)); + + return driver_name; +} + +#ifndef _WIN32 +// core_search_odbc_driver_unix +// This method is meant to be used in a non-Windows environment, +// searching for a particular ODBC driver name in the odbcinst.ini file +// Parameters: +// driver - a valid value in enum ODBC_DRIVER +// Return - a boolean flag that indicates if the specified driver version is found or not +bool core_search_odbc_driver_unix(_In_ ODBC_DRIVER driver) +{ + char szBuf[DEFAULT_CONN_STR_LEN + 1] = { '\0' }; // use a large enough buffer size + WORD cbBufMax = DEFAULT_CONN_STR_LEN; + WORD cbBufOut; + char *pszBuf = szBuf; + + // get all the names of the installed drivers delimited by null characters + if (!SQLGetInstalledDrivers(szBuf, cbBufMax, &cbBufOut)) + return false; + + // search for the derived ODBC driver name based on the given version + std::string driver_name = get_ODBC_driver_name(driver); + do + { + if (strstr(pszBuf, driver_name.c_str()) != 0) + return true; + + // get the next driver + pszBuf = strchr(pszBuf, '\0') + 1; + } while (pszBuf[1] != '\0'); // end when there are two consecutive null characters + + return false; +} +#endif // !_WIN32 + } // namespace // simply add the parsed value to the connection string @@ -1026,27 +1005,36 @@ void conn_null_func::func( connection_option const* /*option*/, zval* /*value*/, { } -void driver_set_func::func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str ) +void driver_set_func::func(_In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str) { - const char* val_str = Z_STRVAL_P( value ); - size_t val_len = Z_STRLEN_P( value ); - std::string driver_option( "" ); - common_conn_str_append_func( option->odbc_name, val_str, val_len, driver_option ); + const char* val_str = Z_STRVAL_P(value); + size_t val_len = Z_STRLEN_P(value); - conn->driver_version = ODBC_DRIVER_UNKNOWN; - for ( short i = DRIVER_VERSION::FIRST; i <= DRIVER_VERSION::LAST && conn->driver_version == ODBC_DRIVER_UNKNOWN; ++i ) { - std::string driver_name = CONNECTION_STRING_DRIVER_NAME[i]; - - if (! driver_name.compare( driver_option ) ) { - conn->driver_version = DRIVER_VERSION( i ); - } + // Check if curly brackets are used, if so, trim them for matching + if (val_len > 0 && val_str[0] == '{' && val_str[val_len - 1] == '}') { + ++val_str; + val_len -= 2; } - CHECK_CUSTOM_ERROR( conn->driver_version == ODBC_DRIVER_UNKNOWN, conn, SQLSRV_ERROR_CONNECT_INVALID_DRIVER, val_str) { + // Check if the user provided driver_option matches any of the acceptable driver names + std::string driver_option(val_str, val_len); + ODBC_DRIVER drivers[] = { ODBC_DRIVER::VER_17, ODBC_DRIVER::VER_18, ODBC_DRIVER::VER_13 }; + + conn->driver_version = ODBC_DRIVER::VER_UNKNOWN; + for (auto &d : drivers) { + std::string name = get_ODBC_driver_name(d); + if (!driver_option.compare(name)) { + conn->driver_version = d; + break; + } + } + + CHECK_CUSTOM_ERROR(conn->driver_version == ODBC_DRIVER::VER_UNKNOWN, conn, SQLSRV_ERROR_CONNECT_INVALID_DRIVER, Z_STRVAL_P(value)) { throw core::CoreException(); } - conn_str += driver_option; + // Append this driver option to the connection string + common_conn_str_append_func(ODBCConnOptions::Driver, driver_option.c_str(), driver_option.length(), conn_str); } void column_encryption_set_func::func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str ) diff --git a/source/shared/core_sqlsrv.h b/source/shared/core_sqlsrv.h index d27c1b40..b111dee8 100644 --- a/source/shared/core_sqlsrv.h +++ b/source/shared/core_sqlsrv.h @@ -1054,14 +1054,13 @@ enum SERVER_VERSION { }; // supported driver versions. -// the latest RTWed ODBC is the first one -enum DRIVER_VERSION { - ODBC_DRIVER_UNKNOWN = -1, - FIRST = 0, - ODBC_DRIVER_17 = FIRST, - ODBC_DRIVER_13 = 1, - ODBC_DRIVER_11 = 2, - LAST = ODBC_DRIVER_11 +// ODBC 17 is the default +enum class ODBC_DRIVER : int +{ + VER_17 = 17, + VER_18 = 18, + VER_13 = 13, + VER_UNKNOWN = 0 }; // forward decl @@ -1097,7 +1096,7 @@ struct sqlsrv_conn : public sqlsrv_context { SERVER_VERSION server_version; // version of the server that we're connected to col_encryption_option ce_option; // holds the details of what are required to enable column encryption - DRIVER_VERSION driver_version; // version of ODBC driver + ODBC_DRIVER driver_version; // version of ODBC driver sqlsrv_malloc_auto_ptr azure_ad_access_token; @@ -1106,7 +1105,7 @@ struct sqlsrv_conn : public sqlsrv_context { sqlsrv_context( h, SQL_HANDLE_DBC, e, drv, encoding ) { server_version = SERVER_VERSION_UNKNOWN; - driver_version = ODBC_DRIVER_UNKNOWN; + driver_version = ODBC_DRIVER::VER_UNKNOWN; } // sqlsrv_conn has no destructor since its allocated using placement new, which requires that the destructor be @@ -1286,7 +1285,6 @@ void core_sqlsrv_get_server_version( _Inout_ sqlsrv_conn* conn, _Inout_ zval *se void core_sqlsrv_get_client_info( _Inout_ sqlsrv_conn* conn, _Out_ zval *client_info ); bool core_is_conn_opt_value_escaped( _Inout_ const char* value, _Inout_ size_t value_len ); size_t core_str_zval_is_true( _Inout_ zval* str_zval ); -bool core_search_odbc_driver_unix( _In_ DRIVER_VERSION driver_version ); bool core_compare_error_state( _In_ sqlsrv_conn* conn, _In_ SQLRETURN r, _In_ const char* error_state ); //********************************************************************************************************************************* diff --git a/test/functional/pdo_sqlsrv/pdo_connect_driver.phpt b/test/functional/pdo_sqlsrv/pdo_connect_driver.phpt index a1a18144..620e74d9 100644 --- a/test/functional/pdo_sqlsrv/pdo_connect_driver.phpt +++ b/test/functional/pdo_sqlsrv/pdo_connect_driver.phpt @@ -23,20 +23,26 @@ testValidValues(); testInvalidValues(); testEncryptedWithODBC(); testWrongODBC(); -echo "Done"; +echo "Done" . PHP_EOL; // end test /////////////////////////// -function connectVerifyOutput($connectionOptions, $expected = '') +function connectVerifyOutput($connectionOptions, $testcase, $expected = null) { global $server, $uid, $pwd; - + try { $conn = new PDO("sqlsrv:server = $server ; $connectionOptions", $uid, $pwd); + if (!is_null($expected)) { + echo "'$testcase' is expected to fail!" . PHP_EOL; + } } catch(PDOException $e) { - if (strpos($e->getMessage(), $expected) === false) { - print_r($e->getMessage()); - echo "\n"; + if (is_null($expected)) { + echo "'$testcase' is expected to pass!" . PHP_EOL; + echo $e->getMessage() . PHP_EOL; + } elseif (strpos($e->getMessage(), $expected) === false) { + echo "The error returned for '$testcase' is unexpected:" . PHP_EOL; + echo $e->getMessage() . PHP_EOL; } } } @@ -44,90 +50,85 @@ function connectVerifyOutput($connectionOptions, $expected = '') function testValidValues() { global $msodbcsqlMaj; - + $value = ""; - // The major version number of ODBC 11 can be 11 or 12 + // The major version number of ODBC 13 can be 13 or 14 // Test with {} switch ($msodbcsqlMaj) { case 17: $value = "{ODBC Driver 17 for SQL Server}"; break; + case 18: + $value = "{ODBC Driver 18 for SQL Server}"; + break; case 14: case 13: $value = "{ODBC Driver 13 for SQL Server}"; break; - case 12: - case 11: - $value = "{ODBC Driver 11 for SQL Server}"; - break; default: $value = "invalid value $msodbcsqlMaj"; } $connectionOptions = "Driver = $value"; - connectVerifyOutput($connectionOptions); - + connectVerifyOutput($connectionOptions, "Driver with curly brackets"); + // Test without {} switch ($msodbcsqlMaj) { case 17: $value = "ODBC Driver 17 for SQL Server"; break; + case 18: + $value = "ODBC Driver 18 for SQL Server"; + break; case 14: case 13: $value = "ODBC Driver 13 for SQL Server"; break; - case 12: - case 11: - $value = "ODBC Driver 11 for SQL Server"; - break; default: $value = "invalid value $msodbcsqlMaj"; } - + $connectionOptions = "Driver = $value"; - connectVerifyOutput($connectionOptions); + connectVerifyOutput($connectionOptions, "Driver without curly brackets"); } function testInvalidValues() { - $values = array("{SQL Server Native Client 11.0}", - "SQL Server Native Client 11.0", - "ODBC Driver 00 for SQL Server", - 123, + $values = array("{SQL Server Native Client 11.0}", + "SQL Server Native Client 11.0", + "ODBC Driver 00 for SQL Server", + 123, false); - + foreach ($values as $value) { $connectionOptions = "Driver = $value"; $expected = "Invalid value $value was specified for Driver option."; - connectVerifyOutput($connectionOptions, $expected); + connectVerifyOutput($connectionOptions, "Invalid driver $value", $expected); } } -function testEncryptedWithODBC() +function testEncryptedWithODBC() { global $msodbcsqlMaj, $server, $uid, $pwd; - + $value = "ODBC Driver 13 for SQL Server"; - $connectionOptions = "Driver = $value; ColumnEncryption = Enabled;"; - + $connectionOptions = "Driver = $value; ColumnEncryption = Enabled;"; + $expected = "The Always Encrypted feature requires Microsoft ODBC Driver 17 for SQL Server"; - connectVerifyOutput($connectionOptions, $expected); + connectVerifyOutput($connectionOptions, "Using ODBC 13 for AE", $expected); } function testWrongODBC() { global $msodbcsqlMaj; - - $value = "ODBC Driver 11 for SQL Server"; - if ($msodbcsqlMaj == 17 || $msodbcsqlMaj < 13) { - $value = "ODBC Driver 13 for SQL Server"; - } + + $value = "ODBC Driver 18 for SQL Server"; $connectionOptions = "Driver = $value;"; $expected = "The specified ODBC Driver is not found."; - - connectVerifyOutput($connectionOptions, $expected); + + connectVerifyOutput($connectionOptions, "Connect with ODBC 18", $expected); } ?> --EXPECT-- -Done \ No newline at end of file +Done diff --git a/test/functional/sqlsrv/sqlsrv_connect_driver.phpt b/test/functional/sqlsrv/sqlsrv_connect_driver.phpt index ee78f0a4..24ae97e8 100644 --- a/test/functional/sqlsrv/sqlsrv_connect_driver.phpt +++ b/test/functional/sqlsrv/sqlsrv_connect_driver.phpt @@ -21,62 +21,66 @@ testValidValues($msodbcsqlMaj, $server, $connectionOptions); testInvalidValues($msodbcsqlMaj, $server, $connectionOptions); testEncryptedWithODBC($msodbcsqlMaj, $server, $connectionOptions); testWrongODBC($msodbcsqlMaj, $server, $connectionOptions); -echo "Done"; +echo "Done\n"; // end test /////////////////////////// -function connectVerifyOutput($server, $connectionOptions, $expected = '') +function connectVerifyOutput($server, $connectionOptions, $testcase, $expected = null) { $conn = sqlsrv_connect($server, $connectionOptions); if ($conn === false) { - if (strpos(sqlsrv_errors($conn)[0]['message'], $expected) === false) { + if (is_null($expected)) { + echo "'$testcase' is expected to pass!\n"; + print_r(sqlsrv_errors()); + } elseif (strpos(sqlsrv_errors($conn)[0]['message'], $expected) === false) { + echo "The error returned for '$testcase' is unexpected:\n"; print_r(sqlsrv_errors()); } + } else if (!is_null($expected)) { + echo "'$testcase' is expected to fail!\n"; } } function testValidValues($msodbcsqlMaj, $server, $connectionOptions) { $value = ""; - // The major version number of ODBC 11 can be 11 or 12 + // The major version number of ODBC 13 can be 13 or 14 // Test with {} switch ($msodbcsqlMaj) { case 17: $value = "{ODBC Driver 17 for SQL Server}"; break; + case 18: + $value = "{ODBC Driver 18 for SQL Server}"; + break; case 14: case 13: $value = "{ODBC Driver 13 for SQL Server}"; break; - case 12: - case 11: - $value = "{ODBC Driver 11 for SQL Server}"; - break; default: $value = "invalid value $msodbcsqlMaj"; } $connectionOptions['Driver']=$value; - connectVerifyOutput($server, $connectionOptions); + connectVerifyOutput($server, $connectionOptions, "Driver with curly brackets"); // Test without {} switch ($msodbcsqlMaj) { case 17: $value = "ODBC Driver 17 for SQL Server"; break; + case 18: + $value = "ODBC Driver 18 for SQL Server"; + break; case 14: case 13: $value = "ODBC Driver 13 for SQL Server"; break; - case 12: - case 11: - $value = "ODBC Driver 11 for SQL Server"; - break; default: $value = "invalid value $msodbcsqlMaj"; } $connectionOptions['Driver']=$value; - connectVerifyOutput($server, $connectionOptions); + connectVerifyOutput($server, $connectionOptions, "Driver without curly brackets"); } function testInvalidValues($msodbcsqlMaj, $server, $connectionOptions) @@ -88,7 +92,7 @@ function testInvalidValues($msodbcsqlMaj, $server, $connectionOptions) foreach ($values as $value) { $connectionOptions['Driver']=$value; $expected = "Invalid value $value was specified for Driver option."; - connectVerifyOutput($server, $connectionOptions, $expected); + connectVerifyOutput($server, $connectionOptions, "Invalid driver $value", $expected); } $values = array(123, false); @@ -96,7 +100,7 @@ function testInvalidValues($msodbcsqlMaj, $server, $connectionOptions) foreach ($values as $value) { $connectionOptions['Driver']=$value; $expected = "Invalid value type for option Driver was specified. String type was expected."; - connectVerifyOutput($server, $connectionOptions, $expected); + connectVerifyOutput($server, $connectionOptions, "Invalid driver $value", $expected); } } @@ -108,22 +112,19 @@ function testEncryptedWithODBC($msodbcsqlMaj, $server, $connectionOptions) $expected = "The Always Encrypted feature requires Microsoft ODBC Driver 17 for SQL Server"; - connectVerifyOutput($server, $connectionOptions, $expected); + connectVerifyOutput($server, $connectionOptions, "Using ODBC 13 for AE", $expected); } function testWrongODBC($msodbcsqlMaj, $server, $connectionOptions) { - $value = "ODBC Driver 11 for SQL Server"; - if ($msodbcsqlMaj == 17 || $msodbcsqlMaj < 13) { - $value = "ODBC Driver 13 for SQL Server"; - } - + $value = "ODBC Driver 18 for SQL Server"; $connectionOptions['Driver']=$value; $expected = "The specified ODBC Driver is not found."; - connectVerifyOutput($server, $connectionOptions, $expected); + connectVerifyOutput($server, $connectionOptions, "Connect with ODBC 18", $expected); } ?> --EXPECT-- Done +