refactored odbc connect, added ae connect error

This commit is contained in:
Hadis-Fard 2017-09-06 10:55:08 -07:00
parent 278a27c3aa
commit f25677e457
4 changed files with 78 additions and 48 deletions

View file

@ -380,7 +380,12 @@ pdo_error PDO_ERRORS[] = {
{
PDO_SQLSRV_ERROR_INVALID_AUTHENTICATION_OPTION,
{ IMSSP, (SQLCHAR*) "Invalid option for the Authentication keyword. Only SqlPassword or ActiveDirectoryPassword is supported.", -73, false }
},
},
{
SQLSRV_AE_ERROR_DRIVER_NOT_INSTALLED,
{ IMSSP, (SQLCHAR*) "This extension requires Microsoft ODBC Driver 17 or higher to "
"communicate with SQL Server with ColumnEncryption attribute enabled.", -74, false }
},
{ UINT_MAX, {} }
};

View file

@ -50,7 +50,7 @@ const int INFO_BUFFER_LEN = 256;
const char* PROCESSOR_ARCH[] = { "x86", "x64", "ia64" };
// ODBC driver name.
const char* CONNECTION_STRING_DRIVER_NAME[] = {"Driver={ODBC Driver 13 for SQL Server};", "Driver={ODBC Driver 11 for SQL Server};"};
const char* 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
const char CONNECTION_STRING_DEFAULT_OPTIONS[] = "Mars_Connection={Yes}";
@ -144,60 +144,29 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
core::SQLAllocHandle( SQL_HANDLE_DBC, *henv, &temp_conn_h TSRMLS_CC );
conn = conn_factory( temp_conn_h, err, driver TSRMLS_CC );
conn->set_func( driver_func );
build_connection_string_and_set_conn_attr(conn, server, uid, pwd, options_ht, valid_conn_opts, driver, conn_options TSRMLS_CC);
for( std::size_t i = DRIVER_VERSION::MIN; i <= DRIVER_VERSION::MAX; ++i ) {
std::string conn_str = conn_options + CONNECTION_STRING_DRIVER_NAME[i];
// We only support UTF-8 encoding for connection string.
// Convert our UTF-8 connection string to UTF-16 before connecting with SQLDriverConnnectW
wconn_len = static_cast<unsigned int>( conn_str.length() + 1 ) * sizeof( SQLWCHAR );
build_connection_string_and_set_conn_attr(conn, server, uid, pwd, options_ht, valid_conn_opts, driver, conn_options TSRMLS_CC);
bool missing_driver_error = false;
if (conn->ce_option.enabled) {
r = core_odbc_connect(conn, conn_options, DRIVER_VERSION::ODBC_DRIVER_13, wconn_string, wconn_len, missing_driver_error);
wconn_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_UTF8, conn_str.c_str(), static_cast<unsigned int>(conn_str.length()), &wconn_len );
CHECK_CUSTOM_ERROR( wconn_string == 0, conn, SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE, get_last_error_message())
{
CHECK_CUSTOM_ERROR(missing_driver_error, conn, SQLSRV_AE_ERROR_DRIVER_NOT_INSTALLED, get_processor_arch()) {
throw core::CoreException();
}
SQLSMALLINT output_conn_size;
#ifndef _WIN32
// unixODBC 2.3.1 requires a non-wide SQLDriverConnect call while pooling enabled.
// connection handle has been allocated using henv_cp, means pooling enabled in a PHP script
if ( henv == &henv_cp )
{
r = SQLDriverConnect( conn->handle(), NULL, (SQLCHAR*)conn_str.c_str(), SQL_NTS, NULL, 0, &output_conn_size, SQL_DRIVER_NOPROMPT );
}
else
{
r = SQLDriverConnectW( conn->handle(), NULL, reinterpret_cast<SQLWCHAR*>( wconn_string.get() ), static_cast<SQLSMALLINT>( wconn_len ), NULL, 0, &output_conn_size, SQL_DRIVER_NOPROMPT );
}
#else
r = SQLDriverConnectW( conn->handle(), NULL, reinterpret_cast<SQLWCHAR*>( wconn_string.get() ), static_cast<SQLSMALLINT>( wconn_len ), NULL, 0, &output_conn_size, SQL_DRIVER_NOPROMPT );
#endif // !_WIN32
else {
// clear the connection string from memory to remove sensitive data (such as a password).
memset( const_cast<char*>(conn_str.c_str()), 0, conn_str.size());
memset( wconn_string, 0, wconn_len * sizeof( SQLWCHAR )); // wconn_len is the number of characters, not bytes
conn_str.clear();
if ( !SQL_SUCCEEDED( r )) {
SQLCHAR state[SQL_SQLSTATE_BUFSIZE];
SQLSMALLINT len;
SQLRETURN sr = SQLGetDiagField( SQL_HANDLE_DBC, conn->handle(), 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len );
bool missing_driver_error = ( SQL_SUCCEEDED( sr ) && state[0] == 'I' && state[1] == 'M' && state[2] == '0' && state[3] == '0' && state[4] == '2' );
for (std::size_t i = DRIVER_VERSION::ODBC_DRIVER_11; i <= DRIVER_VERSION::LAST; ++i) {
r = core_odbc_connect(conn, conn_options, static_cast<DRIVER_VERSION>(i), wconn_string, wconn_len, missing_driver_error);
// if it's a IM002, meaning that the correct ODBC driver is not installed
CHECK_CUSTOM_ERROR(missing_driver_error && ( i == DRIVER_VERSION::MAX ), conn, SQLSRV_ERROR_DRIVER_NOT_INSTALLED, get_processor_arch()) {
CHECK_CUSTOM_ERROR(missing_driver_error && (i == DRIVER_VERSION::LAST), conn, SQLSRV_ERROR_DRIVER_NOT_INSTALLED, get_processor_arch()) {
throw core::CoreException();
}
if ( !missing_driver_error ) {
break;
}
}
else {
conn->driver_version = static_cast<DRIVER_VERSION>( i );
break;
}
} // for
} // for
} // else ce_option enabled
CHECK_SQL_ERROR( r, conn ) {
throw core::CoreException();
@ -256,6 +225,54 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
return return_conn;
}
SQLRETURN core_odbc_connect(_Inout_ sqlsrv_conn* conn, const std::string& conn_options, const DRIVER_VERSION odbc_version, SQLWCHAR* wconn_string, unsigned int& wconn_len,bool missing_driver_error)
{
SQLRETURN r = SQL_SUCCESS;
std::string conn_str = conn_options + CONNECTION_STRING_DRIVER_NAME[odbc_version];
// We only support UTF-8 encoding for connection string.
// Convert our UTF-8 connection string to UTF-16 before connecting with SQLDriverConnnectW
wconn_len = static_cast<unsigned int>(conn_str.length() + 1) * sizeof(SQLWCHAR);
wconn_string = utf16_string_from_mbcs_string(SQLSRV_ENCODING_UTF8, conn_str.c_str(), static_cast<unsigned int>(conn_str.length()), &wconn_len);
CHECK_CUSTOM_ERROR(wconn_string == 0, conn, SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE, get_last_error_message())
{
throw core::CoreException();
}
SQLSMALLINT output_conn_size;
#ifndef _WIN32
// unixODBC 2.3.1 requires a non-wide SQLDriverConnect call while pooling enabled.
// connection handle has been allocated using henv_cp, means pooling enabled in a PHP script
if (henv == &henv_cp)
{
r = SQLDriverConnect(conn->handle(), NULL, (SQLCHAR*)conn_str.c_str(), SQL_NTS, NULL, 0, &output_conn_size, SQL_DRIVER_NOPROMPT);
}
else
{
r = SQLDriverConnectW(conn->handle(), NULL, reinterpret_cast<SQLWCHAR*>(wconn_string.get()), static_cast<SQLSMALLINT>(wconn_len), NULL, 0, &output_conn_size, SQL_DRIVER_NOPROMPT);
}
#else
r = SQLDriverConnectW(conn->handle(), NULL, reinterpret_cast<SQLWCHAR*>(wconn_string), static_cast<SQLSMALLINT>(wconn_len), NULL, 0, &output_conn_size, SQL_DRIVER_NOPROMPT);
#endif // !_WIN32
// clear the connection string from memory to remove sensitive data (such as a password).
memset(const_cast<char*>(conn_str.c_str()), 0, conn_str.size());
memset(wconn_string, 0, wconn_len * sizeof(SQLWCHAR)); // wconn_len is the number of characters, not bytes
conn_str.clear();
if (SQL_SUCCEEDED(r)) {
conn->driver_version = static_cast<DRIVER_VERSION>(odbc_version);
}
else {
SQLCHAR state[SQL_SQLSTATE_BUFSIZE];
SQLSMALLINT len;
SQLRETURN sr = SQLGetDiagField(SQL_HANDLE_DBC, conn->handle(), 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len);
missing_driver_error = (SQL_SUCCEEDED(sr) && state[0] == 'I' && state[1] == 'M' && state[2] == '0' && state[3] == '0' && state[4] == '2');
}
return r;
}
// core_sqlsrv_begin_transaction

View file

@ -1034,10 +1034,11 @@ enum SERVER_VERSION {
// supported driver versions.
enum DRIVER_VERSION : size_t {
MIN = 0,
ODBC_DRIVER_13 = MIN,
ODBC_DRIVER_11 = 1,
MAX = ODBC_DRIVER_11,
FIRST = 0,
ODBC_DRIVER_17 = FIRST,
ODBC_DRIVER_13 = 1,
ODBC_DRIVER_11 = 2,
LAST = ODBC_DRIVER_11
};
// forward decl
@ -1205,6 +1206,7 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
_Inout_z_ const char* server, _Inout_opt_z_ const char* uid, _Inout_opt_z_ const char* pwd,
_Inout_opt_ HashTable* options_ht, _In_ error_callback err, _In_ const connection_option valid_conn_opts[],
_In_ void* driver, _In_z_ const char* driver_func TSRMLS_DC );
SQLRETURN core_odbc_connect(_Inout_ sqlsrv_conn* conn, const std::string& conn_options, const DRIVER_VERSION odbc_version, SQLWCHAR* wconn_string, unsigned int& wconn_len, bool missing_driver_error);
void core_sqlsrv_close( _Inout_opt_ sqlsrv_conn* conn TSRMLS_DC );
void core_sqlsrv_prepare( _Inout_ sqlsrv_stmt* stmt, _In_reads_bytes_(sql_len) const char* sql, _In_ SQLLEN sql_len TSRMLS_DC );
void core_sqlsrv_begin_transaction( _Inout_ sqlsrv_conn* conn TSRMLS_DC );
@ -1608,6 +1610,7 @@ enum SQLSRV_ERROR_CODES {
SQLSRV_ERROR_ODBC,
SQLSRV_ERROR_DRIVER_NOT_INSTALLED,
SQLSRV_AE_ERROR_DRIVER_NOT_INSTALLED,
SQLSRV_ERROR_ZEND_HASH,
SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE,
SQLSRV_ERROR_INVALID_PARAMETER_SQLTYPE,

View file

@ -376,6 +376,11 @@ ss_error SS_ERRORS[] = {
SS_SQLSRV_WARNING_FIELD_NAME_EMPTY,
{ SSPWARN, (SQLCHAR*)"An empty field name was skipped by sqlsrv_fetch_object.", -100, false }
},
{
SQLSRV_AE_ERROR_DRIVER_NOT_INSTALLED,
{ IMSSP, (SQLCHAR*) "This extension requires Microsoft ODBC Driver 17 or higher to "
"communicate with SQL Server with ColumnEncryption attribute enabled.", -105, false }
},
// terminate the list of errors/warnings
{ UINT_MAX, {} }