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
|
// length for name of keystore used in CEKeyStoreData
|
||||||
const int MAX_CE_NAME_LEN = 260;
|
const int MAX_CE_NAME_LEN = 260;
|
||||||
|
|
||||||
// ODBC driver names.
|
// ODBC driver name
|
||||||
// the order of this list should match the order of DRIVER_VERSION enum
|
const char ODBC_DRIVER_NAME[] = "ODBC Driver %d for SQL Server";
|
||||||
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};" };
|
|
||||||
|
|
||||||
// default options if only the server is specified
|
// default options if only the server is specified
|
||||||
const char CONNECTION_STRING_DEFAULT_OPTIONS[] = "Mars_Connection={Yes};";
|
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 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 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);
|
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
|
// 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 );
|
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
|
// 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
|
// 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
|
#ifndef _WIN32
|
||||||
if( conn->driver_version != ODBC_DRIVER_UNKNOWN ) {
|
|
||||||
// check if the ODBC driver actually exists, if not, throw an exception
|
// 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();
|
throw core::CoreException();
|
||||||
}
|
}
|
||||||
|
// if the driver exists, connect
|
||||||
r = core_odbc_connect( conn, conn_str, is_pooled );
|
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
|
|
||||||
#else
|
#else
|
||||||
if( conn->driver_version != ODBC_DRIVER_UNKNOWN ) {
|
// try to connect with the specified ODBC driver
|
||||||
r = core_odbc_connect( conn, conn_str, is_pooled );
|
r = core_odbc_connect(conn, conn_str, is_pooled);
|
||||||
|
|
||||||
// check if the specified ODBC driver is there
|
// 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 ) {
|
CHECK_CUSTOM_ERROR(core_compare_error_state(conn, r, "IM002"), conn, SQLSRV_ERROR_SPECIFIED_DRIVER_NOT_FOUND) {
|
||||||
throw core::CoreException();
|
throw core::CoreException();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if( conn->ce_option.enabled ) {
|
// ODBC driver not specified, so check ODBC 17 first then ODBC 18 and/or ODBC 13
|
||||||
// driver not specified, so connect using ODBC 17
|
// If column encryption is enabled, check up to ODBC 18
|
||||||
conn_str = conn_str + CONNECTION_STRING_DRIVER_NAME[ODBC_DRIVER_17];
|
ODBC_DRIVER drivers[] = { ODBC_DRIVER::VER_17, ODBC_DRIVER::VER_18, ODBC_DRIVER::VER_13 };
|
||||||
r = core_odbc_connect( conn, conn_str, is_pooled );
|
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
|
// here it means that none of the supported ODBC drivers is found
|
||||||
CHECK_CUSTOM_ERROR( core_compare_error_state( conn, r, "IM002" ) , conn, SQLSRV_ERROR_CE_DRIVER_REQUIRED, get_processor_arch() ) {
|
CHECK_CUSTOM_ERROR(true, conn, SQLSRV_ERROR_DRIVER_NOT_INSTALLED, get_processor_arch()) {
|
||||||
throw core::CoreException();
|
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
|
// time to free the access token, if not null
|
||||||
if (conn->azure_ad_access_token) {
|
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 ) ) );
|
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
|
// core_odbc_connect
|
||||||
// calls odbc connect API to establish the connection to server
|
// calls odbc connect API to establish the connection to server
|
||||||
// Parameters:
|
// Parameters:
|
||||||
|
@ -1010,6 +947,48 @@ void common_conn_str_append_func( _In_z_ const char* odbc_name, _In_reads_(val_l
|
||||||
conn_str += "};";
|
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
|
} // namespace
|
||||||
|
|
||||||
// simply add the parsed value to the connection string
|
// 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 );
|
const char* val_str = Z_STRVAL_P(value);
|
||||||
size_t val_len = Z_STRLEN_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 );
|
|
||||||
|
|
||||||
conn->driver_version = ODBC_DRIVER_UNKNOWN;
|
// Check if curly brackets are used, if so, trim them for matching
|
||||||
for ( short i = DRIVER_VERSION::FIRST; i <= DRIVER_VERSION::LAST && conn->driver_version == ODBC_DRIVER_UNKNOWN; ++i ) {
|
if (val_len > 0 && val_str[0] == '{' && val_str[val_len - 1] == '}') {
|
||||||
std::string driver_name = CONNECTION_STRING_DRIVER_NAME[i];
|
++val_str;
|
||||||
|
val_len -= 2;
|
||||||
if (! driver_name.compare( driver_option ) ) {
|
|
||||||
conn->driver_version = DRIVER_VERSION( i );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
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 )
|
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.
|
// supported driver versions.
|
||||||
// the latest RTWed ODBC is the first one
|
// ODBC 17 is the default
|
||||||
enum DRIVER_VERSION {
|
enum class ODBC_DRIVER : int
|
||||||
ODBC_DRIVER_UNKNOWN = -1,
|
{
|
||||||
FIRST = 0,
|
VER_17 = 17,
|
||||||
ODBC_DRIVER_17 = FIRST,
|
VER_18 = 18,
|
||||||
ODBC_DRIVER_13 = 1,
|
VER_13 = 13,
|
||||||
ODBC_DRIVER_11 = 2,
|
VER_UNKNOWN = 0
|
||||||
LAST = ODBC_DRIVER_11
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// forward decl
|
// 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
|
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
|
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;
|
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 )
|
sqlsrv_context( h, SQL_HANDLE_DBC, e, drv, encoding )
|
||||||
{
|
{
|
||||||
server_version = SERVER_VERSION_UNKNOWN;
|
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
|
// 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 );
|
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 );
|
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 );
|
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 );
|
bool core_compare_error_state( _In_ sqlsrv_conn* conn, _In_ SQLRETURN r, _In_ const char* error_state );
|
||||||
|
|
||||||
//*********************************************************************************************************************************
|
//*********************************************************************************************************************************
|
||||||
|
|
|
@ -23,20 +23,26 @@ testValidValues();
|
||||||
testInvalidValues();
|
testInvalidValues();
|
||||||
testEncryptedWithODBC();
|
testEncryptedWithODBC();
|
||||||
testWrongODBC();
|
testWrongODBC();
|
||||||
echo "Done";
|
echo "Done" . PHP_EOL;
|
||||||
// end test
|
// end test
|
||||||
|
|
||||||
///////////////////////////
|
///////////////////////////
|
||||||
function connectVerifyOutput($connectionOptions, $expected = '')
|
function connectVerifyOutput($connectionOptions, $testcase, $expected = null)
|
||||||
{
|
{
|
||||||
global $server, $uid, $pwd;
|
global $server, $uid, $pwd;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$conn = new PDO("sqlsrv:server = $server ; $connectionOptions", $uid, $pwd);
|
$conn = new PDO("sqlsrv:server = $server ; $connectionOptions", $uid, $pwd);
|
||||||
|
if (!is_null($expected)) {
|
||||||
|
echo "'$testcase' is expected to fail!" . PHP_EOL;
|
||||||
|
}
|
||||||
} catch(PDOException $e) {
|
} catch(PDOException $e) {
|
||||||
if (strpos($e->getMessage(), $expected) === false) {
|
if (is_null($expected)) {
|
||||||
print_r($e->getMessage());
|
echo "'$testcase' is expected to pass!" . PHP_EOL;
|
||||||
echo "\n";
|
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()
|
function testValidValues()
|
||||||
{
|
{
|
||||||
global $msodbcsqlMaj;
|
global $msodbcsqlMaj;
|
||||||
|
|
||||||
$value = "";
|
$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 {}
|
// Test with {}
|
||||||
switch ($msodbcsqlMaj) {
|
switch ($msodbcsqlMaj) {
|
||||||
case 17:
|
case 17:
|
||||||
$value = "{ODBC Driver 17 for SQL Server}";
|
$value = "{ODBC Driver 17 for SQL Server}";
|
||||||
break;
|
break;
|
||||||
|
case 18:
|
||||||
|
$value = "{ODBC Driver 18 for SQL Server}";
|
||||||
|
break;
|
||||||
case 14:
|
case 14:
|
||||||
case 13:
|
case 13:
|
||||||
$value = "{ODBC Driver 13 for SQL Server}";
|
$value = "{ODBC Driver 13 for SQL Server}";
|
||||||
break;
|
break;
|
||||||
case 12:
|
|
||||||
case 11:
|
|
||||||
$value = "{ODBC Driver 11 for SQL Server}";
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
$value = "invalid value $msodbcsqlMaj";
|
$value = "invalid value $msodbcsqlMaj";
|
||||||
}
|
}
|
||||||
$connectionOptions = "Driver = $value";
|
$connectionOptions = "Driver = $value";
|
||||||
connectVerifyOutput($connectionOptions);
|
connectVerifyOutput($connectionOptions, "Driver with curly brackets");
|
||||||
|
|
||||||
// Test without {}
|
// Test without {}
|
||||||
switch ($msodbcsqlMaj) {
|
switch ($msodbcsqlMaj) {
|
||||||
case 17:
|
case 17:
|
||||||
$value = "ODBC Driver 17 for SQL Server";
|
$value = "ODBC Driver 17 for SQL Server";
|
||||||
break;
|
break;
|
||||||
|
case 18:
|
||||||
|
$value = "ODBC Driver 18 for SQL Server";
|
||||||
|
break;
|
||||||
case 14:
|
case 14:
|
||||||
case 13:
|
case 13:
|
||||||
$value = "ODBC Driver 13 for SQL Server";
|
$value = "ODBC Driver 13 for SQL Server";
|
||||||
break;
|
break;
|
||||||
case 12:
|
|
||||||
case 11:
|
|
||||||
$value = "ODBC Driver 11 for SQL Server";
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
$value = "invalid value $msodbcsqlMaj";
|
$value = "invalid value $msodbcsqlMaj";
|
||||||
}
|
}
|
||||||
|
|
||||||
$connectionOptions = "Driver = $value";
|
$connectionOptions = "Driver = $value";
|
||||||
connectVerifyOutput($connectionOptions);
|
connectVerifyOutput($connectionOptions, "Driver without curly brackets");
|
||||||
}
|
}
|
||||||
|
|
||||||
function testInvalidValues()
|
function testInvalidValues()
|
||||||
{
|
{
|
||||||
$values = array("{SQL Server Native Client 11.0}",
|
$values = array("{SQL Server Native Client 11.0}",
|
||||||
"SQL Server Native Client 11.0",
|
"SQL Server Native Client 11.0",
|
||||||
"ODBC Driver 00 for SQL Server",
|
"ODBC Driver 00 for SQL Server",
|
||||||
123,
|
123,
|
||||||
false);
|
false);
|
||||||
|
|
||||||
foreach ($values as $value) {
|
foreach ($values as $value) {
|
||||||
$connectionOptions = "Driver = $value";
|
$connectionOptions = "Driver = $value";
|
||||||
$expected = "Invalid value $value was specified for Driver option.";
|
$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;
|
global $msodbcsqlMaj, $server, $uid, $pwd;
|
||||||
|
|
||||||
$value = "ODBC Driver 13 for SQL Server";
|
$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";
|
$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()
|
function testWrongODBC()
|
||||||
{
|
{
|
||||||
global $msodbcsqlMaj;
|
global $msodbcsqlMaj;
|
||||||
|
|
||||||
$value = "ODBC Driver 11 for SQL Server";
|
$value = "ODBC Driver 18 for SQL Server";
|
||||||
if ($msodbcsqlMaj == 17 || $msodbcsqlMaj < 13) {
|
|
||||||
$value = "ODBC Driver 13 for SQL Server";
|
|
||||||
}
|
|
||||||
$connectionOptions = "Driver = $value;";
|
$connectionOptions = "Driver = $value;";
|
||||||
$expected = "The specified ODBC Driver is not found.";
|
$expected = "The specified ODBC Driver is not found.";
|
||||||
|
|
||||||
connectVerifyOutput($connectionOptions, $expected);
|
connectVerifyOutput($connectionOptions, "Connect with ODBC 18", $expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
--EXPECT--
|
--EXPECT--
|
||||||
Done
|
Done
|
||||||
|
|
|
@ -21,62 +21,66 @@ testValidValues($msodbcsqlMaj, $server, $connectionOptions);
|
||||||
testInvalidValues($msodbcsqlMaj, $server, $connectionOptions);
|
testInvalidValues($msodbcsqlMaj, $server, $connectionOptions);
|
||||||
testEncryptedWithODBC($msodbcsqlMaj, $server, $connectionOptions);
|
testEncryptedWithODBC($msodbcsqlMaj, $server, $connectionOptions);
|
||||||
testWrongODBC($msodbcsqlMaj, $server, $connectionOptions);
|
testWrongODBC($msodbcsqlMaj, $server, $connectionOptions);
|
||||||
echo "Done";
|
echo "Done\n";
|
||||||
// end test
|
// end test
|
||||||
|
|
||||||
///////////////////////////
|
///////////////////////////
|
||||||
function connectVerifyOutput($server, $connectionOptions, $expected = '')
|
function connectVerifyOutput($server, $connectionOptions, $testcase, $expected = null)
|
||||||
{
|
{
|
||||||
$conn = sqlsrv_connect($server, $connectionOptions);
|
$conn = sqlsrv_connect($server, $connectionOptions);
|
||||||
if ($conn === false) {
|
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());
|
print_r(sqlsrv_errors());
|
||||||
}
|
}
|
||||||
|
} else if (!is_null($expected)) {
|
||||||
|
echo "'$testcase' is expected to fail!\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function testValidValues($msodbcsqlMaj, $server, $connectionOptions)
|
function testValidValues($msodbcsqlMaj, $server, $connectionOptions)
|
||||||
{
|
{
|
||||||
$value = "";
|
$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 {}
|
// Test with {}
|
||||||
switch ($msodbcsqlMaj) {
|
switch ($msodbcsqlMaj) {
|
||||||
case 17:
|
case 17:
|
||||||
$value = "{ODBC Driver 17 for SQL Server}";
|
$value = "{ODBC Driver 17 for SQL Server}";
|
||||||
break;
|
break;
|
||||||
|
case 18:
|
||||||
|
$value = "{ODBC Driver 18 for SQL Server}";
|
||||||
|
break;
|
||||||
case 14:
|
case 14:
|
||||||
case 13:
|
case 13:
|
||||||
$value = "{ODBC Driver 13 for SQL Server}";
|
$value = "{ODBC Driver 13 for SQL Server}";
|
||||||
break;
|
break;
|
||||||
case 12:
|
|
||||||
case 11:
|
|
||||||
$value = "{ODBC Driver 11 for SQL Server}";
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
$value = "invalid value $msodbcsqlMaj";
|
$value = "invalid value $msodbcsqlMaj";
|
||||||
}
|
}
|
||||||
$connectionOptions['Driver']=$value;
|
$connectionOptions['Driver']=$value;
|
||||||
connectVerifyOutput($server, $connectionOptions);
|
connectVerifyOutput($server, $connectionOptions, "Driver with curly brackets");
|
||||||
|
|
||||||
// Test without {}
|
// Test without {}
|
||||||
switch ($msodbcsqlMaj) {
|
switch ($msodbcsqlMaj) {
|
||||||
case 17:
|
case 17:
|
||||||
$value = "ODBC Driver 17 for SQL Server";
|
$value = "ODBC Driver 17 for SQL Server";
|
||||||
break;
|
break;
|
||||||
|
case 18:
|
||||||
|
$value = "ODBC Driver 18 for SQL Server";
|
||||||
|
break;
|
||||||
case 14:
|
case 14:
|
||||||
case 13:
|
case 13:
|
||||||
$value = "ODBC Driver 13 for SQL Server";
|
$value = "ODBC Driver 13 for SQL Server";
|
||||||
break;
|
break;
|
||||||
case 12:
|
|
||||||
case 11:
|
|
||||||
$value = "ODBC Driver 11 for SQL Server";
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
$value = "invalid value $msodbcsqlMaj";
|
$value = "invalid value $msodbcsqlMaj";
|
||||||
}
|
}
|
||||||
|
|
||||||
$connectionOptions['Driver']=$value;
|
$connectionOptions['Driver']=$value;
|
||||||
connectVerifyOutput($server, $connectionOptions);
|
connectVerifyOutput($server, $connectionOptions, "Driver without curly brackets");
|
||||||
}
|
}
|
||||||
|
|
||||||
function testInvalidValues($msodbcsqlMaj, $server, $connectionOptions)
|
function testInvalidValues($msodbcsqlMaj, $server, $connectionOptions)
|
||||||
|
@ -88,7 +92,7 @@ function testInvalidValues($msodbcsqlMaj, $server, $connectionOptions)
|
||||||
foreach ($values as $value) {
|
foreach ($values as $value) {
|
||||||
$connectionOptions['Driver']=$value;
|
$connectionOptions['Driver']=$value;
|
||||||
$expected = "Invalid value $value was specified for Driver option.";
|
$expected = "Invalid value $value was specified for Driver option.";
|
||||||
connectVerifyOutput($server, $connectionOptions, $expected);
|
connectVerifyOutput($server, $connectionOptions, "Invalid driver $value", $expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
$values = array(123, false);
|
$values = array(123, false);
|
||||||
|
@ -96,7 +100,7 @@ function testInvalidValues($msodbcsqlMaj, $server, $connectionOptions)
|
||||||
foreach ($values as $value) {
|
foreach ($values as $value) {
|
||||||
$connectionOptions['Driver']=$value;
|
$connectionOptions['Driver']=$value;
|
||||||
$expected = "Invalid value type for option Driver was specified. String type was expected.";
|
$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";
|
$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)
|
function testWrongODBC($msodbcsqlMaj, $server, $connectionOptions)
|
||||||
{
|
{
|
||||||
$value = "ODBC Driver 11 for SQL Server";
|
$value = "ODBC Driver 18 for SQL Server";
|
||||||
if ($msodbcsqlMaj == 17 || $msodbcsqlMaj < 13) {
|
|
||||||
$value = "ODBC Driver 13 for SQL Server";
|
|
||||||
}
|
|
||||||
|
|
||||||
$connectionOptions['Driver']=$value;
|
$connectionOptions['Driver']=$value;
|
||||||
$expected = "The specified ODBC Driver is not found.";
|
$expected = "The specified ODBC Driver is not found.";
|
||||||
|
|
||||||
connectVerifyOutput($server, $connectionOptions, $expected);
|
connectVerifyOutput($server, $connectionOptions, "Connect with ODBC 18", $expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
--EXPECT--
|
--EXPECT--
|
||||||
Done
|
Done
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue