bool convert_string_from_utf16( SQLSRV_ENCODING encoding, const wchar_t* inString, SQLINTEGER cchInLen, char** outString, SQLINTEGER& cchOutLen ) { SQLSRV_ASSERT( inString != NULL, "Input string must be specified" ); SQLSRV_ASSERT( outString != NULL, "Output buffer pointer must be specified" ); SQLSRV_ASSERT( *outString == NULL, "Output buffer pointer must not be set" ); if (cchInLen == 0 && inString[0] == L'\0') { *outString = reinterpret_cast<char*>( sqlsrv_malloc ( 1 ) ); *outString[0] = '\0'; cchOutLen = 0; return true; } // flags set to 0 by default, which means that any invalid characters are dropped rather than causing // an error. This happens only on XP. DWORD flags = 0; if( encoding == CP_UTF8 && g_osversion.dwMajorVersion >= SQLSRV_OS_VISTA_OR_LATER ) { // Vista (and later) will detect invalid UTF-16 characters and raise an error. flags = WC_ERR_INVALID_CHARS; } // calculate the number of characters needed cchOutLen = WideCharToMultiByte( encoding, flags, inString, cchInLen, NULL, 0, NULL, NULL ); if( cchOutLen == 0 ) { return false; } // Create a buffer to fit the encoded string char* newString = reinterpret_cast<char*>( sqlsrv_malloc( cchOutLen + 1 /* NULL char*/ )); int rc = WideCharToMultiByte( encoding, flags, inString, cchInLen, newString, cchOutLen, NULL, NULL ); if( rc == 0 ) { cchOutLen = 0; sqlsrv_free( newString ); return false; } *outString = newString; newString[cchOutLen] = '\0'; // null terminate the encoded string return true; }
// pdo error handler for the dbh context. bool pdo_sqlsrv_handle_dbh_error( sqlsrv_context& ctx, unsigned int sqlsrv_error_code, bool warning TSRMLS_DC, va_list* print_args ) { pdo_dbh_t* dbh = reinterpret_cast<pdo_dbh_t*>( ctx.driver()); SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_handle_dbh_error: Null dbh passed" ); sqlsrv_error_auto_ptr error; if( sqlsrv_error_code != SQLSRV_ERROR_ODBC ) { core_sqlsrv_format_driver_error( ctx, get_error_message( sqlsrv_error_code ), error, SEV_ERROR TSRMLS_CC, print_args ); } else { bool err = core_sqlsrv_get_odbc_error( ctx, 1, error, SEV_ERROR TSRMLS_CC ); SQLSRV_ASSERT( err == true, "No ODBC error was found" ); } SQLSRV_STATIC_ASSERT( sizeof( error->sqlstate ) <= sizeof( dbh->error_code )); strcpy_s( dbh->error_code, sizeof( dbh->error_code ), reinterpret_cast<const char*>( error->sqlstate )); switch( dbh->error_mode ) { case PDO_ERRMODE_EXCEPTION: if( !warning ) { pdo_sqlsrv_throw_exception( error TSRMLS_CC ); } ctx.set_last_error( error ); break; case PDO_ERRMODE_WARNING: if( !warning ) { unsigned int msg_len = strlen( reinterpret_cast<const char*>( error->native_message )) + SQL_SQLSTATE_BUFSIZE + MAX_DIGITS + 1; sqlsrv_malloc_auto_ptr<char> msg; msg = static_cast<char*>( sqlsrv_malloc( msg_len )); core_sqlsrv_format_message( msg, msg_len, WARNING_TEMPLATE, error->sqlstate, error->native_code, error->native_message ); php_error( E_WARNING, msg ); sqlsrv_free( msg ); } ctx.set_last_error( error ); break; case PDO_ERRMODE_SILENT: ctx.set_last_error( error ); break; default: DIE( "Unknown error mode. %1!d!", dbh->error_mode ); break; } // return error ignored = true for warnings. return ( warning ? true : false ); }
// format and return a driver specfic error void core_sqlsrv_format_driver_error( sqlsrv_context& ctx, sqlsrv_error_const const* custom_error, sqlsrv_error_auto_ptr& formatted_error, logging_severity severity TSRMLS_DC, va_list* args ) { // allocate space for the formatted message formatted_error = new (sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error(); formatted_error->sqlstate = reinterpret_cast<SQLCHAR*>( sqlsrv_malloc( SQL_SQLSTATE_BUFSIZE )); formatted_error->native_message = reinterpret_cast<SQLCHAR*>( sqlsrv_malloc( SQL_MAX_MESSAGE_LENGTH + 1 )); DWORD rc = FormatMessage( FORMAT_MESSAGE_FROM_STRING, reinterpret_cast<LPSTR>( custom_error->native_message ), 0, 0, reinterpret_cast<LPSTR>( formatted_error->native_message ), SQL_MAX_MESSAGE_LENGTH, args ); if( rc == 0 ) { strcpy_s( reinterpret_cast<char*>( formatted_error->native_message ), SQL_MAX_MESSAGE_LENGTH, reinterpret_cast<char*>( INTERNAL_FORMAT_ERROR )); } strcpy_s( reinterpret_cast<char*>( formatted_error->sqlstate ), SQL_SQLSTATE_BUFSIZE, reinterpret_cast<char*>( custom_error->sqlstate )); formatted_error->native_code = custom_error->native_code; // log the error LOG( severity, "%1!s!: SQLSTATE = %2!s!", ctx.func(), formatted_error->sqlstate ); LOG( severity, "%1!s!: error code = %2!d!", ctx.func(), formatted_error->native_code ); LOG( severity, "%1!s!: message = %2!s!", ctx.func(), formatted_error->native_message ); }
// thin wrapper around convert_string_from_default_encoding that handles // allocation of the destination string. An empty string passed in returns // failure since it's a failure case for convert_string_from_default_encoding. wchar_t* utf16_string_from_mbcs_string( SQLSRV_ENCODING php_encoding, const char* mbcs_string, unsigned int mbcs_len, unsigned int* utf16_len ) { *utf16_len = (mbcs_len + 1); wchar_t* utf16_string = reinterpret_cast<wchar_t*>( sqlsrv_malloc( *utf16_len * sizeof( wchar_t ))); *utf16_len = convert_string_from_default_encoding( php_encoding, mbcs_string, mbcs_len, utf16_string, *utf16_len ); if( *utf16_len == 0 ) { // we preserve the error and reset it because sqlsrv_free resets the last error DWORD last_error = GetLastError(); sqlsrv_free( utf16_string ); SetLastError( last_error ); return NULL; } return utf16_string; }
// Validate a given DSN keyword. void conn_string_parser::validate_key(const char *key, int key_len TSRMLS_DC ) { int new_len = discard_trailing_white_spaces( key, key_len ); for( int i=0; PDO_CONN_OPTS[ i ].conn_option_key != SQLSRV_CONN_OPTION_INVALID; ++i ) { // discard the null terminator. if( new_len == ( PDO_CONN_OPTS[ i ].sqlsrv_len - 1 ) && !_strnicmp( key, PDO_CONN_OPTS[ i ].sqlsrv_name, new_len )) { this->current_key = PDO_CONN_OPTS[ i ].conn_option_key; this->current_key_name = PDO_CONN_OPTS[ i ].sqlsrv_name; return; } } // encountered an invalid key, throw error. sqlsrv_malloc_auto_ptr<char> key_name; key_name = static_cast<char*>( sqlsrv_malloc( new_len + 1 )); memcpy( key_name, key, new_len ); key_name[ new_len ] = '\0'; THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_INVALID_DSN_KEY, key_name ); }
bool core_sqlsrv_get_odbc_error( sqlsrv_context& ctx, int record_number, sqlsrv_error_auto_ptr& error, logging_severity severity TSRMLS_DC ) { SQLHANDLE h = ctx.handle(); SQLSMALLINT h_type = ctx.handle_type(); if( h == NULL ) { return false; } zval* ssphp_z = NULL; int zr = SUCCESS; zval* temp = NULL; SQLRETURN r = SQL_SUCCESS; SQLSMALLINT wmessage_len = 0; SQLWCHAR wsqlstate[ SQL_SQLSTATE_BUFSIZE ]; SQLWCHAR wnative_message[ SQL_MAX_MESSAGE_LENGTH + 1 ]; SQLSRV_ENCODING enc = ctx.encoding(); switch( h_type ) { case SQL_HANDLE_STMT: { sqlsrv_stmt* stmt = static_cast<sqlsrv_stmt*>( &ctx ); if( stmt->current_results != NULL ) { error = stmt->current_results->get_diag_rec( record_number ); // don't use the CHECK* macros here since it will trigger reentry into the error handling system if( error == NULL ) { return false; } break; } // convert the error into the encoding of the context if( enc == SQLSRV_ENCODING_DEFAULT ) { enc = stmt->conn->encoding(); } } default: error = new ( sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error(); r = SQLGetDiagRecW( h_type, h, record_number, wsqlstate, &error->native_code, wnative_message, SQL_MAX_MESSAGE_LENGTH + 1, &wmessage_len ); // don't use the CHECK* macros here since it will trigger reentry into the error handling system if( !SQL_SUCCEEDED( r ) || r == SQL_NO_DATA ) { return false; } SQLINTEGER sqlstate_len = 0; convert_string_from_utf16(enc, wsqlstate, sizeof(wsqlstate), (char**)&error->sqlstate, sqlstate_len); SQLINTEGER message_len = 0; convert_string_from_utf16(enc, wnative_message, wmessage_len, (char**)&error->native_message, message_len); break; } // log the error first LOG( severity, "%1!s!: SQLSTATE = %2!s!", ctx.func(), error->sqlstate ); LOG( severity, "%1!s!: error code = %2!d!", ctx.func(), error->native_code ); LOG( severity, "%1!s!: message = %2!s!", ctx.func(), error->native_message ); error->format = false; return true; }