Replaced ODBC Driver 11 with 18 (#1347)
This commit is contained in:
parent
c395539132
commit
c9a5e8eeaa
|
@ -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<std::string> 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<char*>( 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<int>(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 )
|
||||
|
|
|
@ -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<ACCESSTOKEN> 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 );
|
||||
|
||||
//*********************************************************************************************************************************
|
||||
|
|
|
@ -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
|
||||
Done
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue