PageRenderTime 46ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/sqlsrv/core_util.cpp

#
C++ | 341 lines | 206 code | 61 blank | 74 comment | 36 complexity | ed5b2dfb93642e5fe6a71709f9fa8907 MD5 | raw file
  1. //---------------------------------------------------------------------------------------------------------------------------------
  2. // File: core_util.cpp
  3. //
  4. // Contents: Utility functions used by both connection or statement functions for both the PDO and sqlsrv drivers
  5. //
  6. // Comments: Mostly error handling and some type handling
  7. //
  8. // Copyright Microsoft Corporation
  9. //
  10. // Licensed under the Apache License, Version 2.0 (the "License");
  11. // you may not use this file except in compliance with the License.
  12. //
  13. // You may obtain a copy of the License at:
  14. // http://www.apache.org/licenses/LICENSE-2.0
  15. //
  16. // Unless required by applicable law or agreed to in writing, software
  17. // distributed under the License is distributed on an "AS IS" BASIS,
  18. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  19. // See the License for the specific language governing permissions and
  20. // limitations under the License.
  21. //---------------------------------------------------------------------------------------------------------------------------------
  22. #include "core_sqlsrv.h"
  23. #include <windows.h>
  24. namespace {
  25. // *** internal constants ***
  26. log_callback g_driver_log;
  27. // internal error that says that FormatMessage failed
  28. SQLCHAR INTERNAL_FORMAT_ERROR[] = "An internal error occurred. FormatMessage failed writing an error message.";
  29. // buffer used to hold a formatted log message prior to actually logging it.
  30. char last_err_msg[ 2048 ]; // 2k to hold the error messages
  31. // routine used by utf16_string_from_mbcs_string
  32. unsigned int convert_string_from_default_encoding( unsigned int php_encoding, __in_bcount(mbcs_len) char const* mbcs_in_string,
  33. unsigned int mbcs_len,
  34. __out_ecount(utf16_len) __transfer( mbcs_in_string ) wchar_t* utf16_out_string,
  35. unsigned int utf16_len );
  36. }
  37. // SQLSTATE for all internal errors
  38. SQLCHAR IMSSP[] = "IMSSP";
  39. // SQLSTATE for all internal warnings
  40. SQLCHAR SSPWARN[] = "01SSP";
  41. // write to the php log if the severity and subsystem match the filters currently set in the INI or
  42. // the script (sqlsrv_configure).
  43. void write_to_log( unsigned int severity TSRMLS_DC, const char* msg, ...)
  44. {
  45. SQLSRV_ASSERT( !(g_driver_log == NULL), "Must register a driver log function." );
  46. va_list args;
  47. va_start( args, msg );
  48. g_driver_log( severity TSRMLS_CC, msg, &args );
  49. va_end( args );
  50. }
  51. void core_sqlsrv_register_logger( log_callback driver_logger )
  52. {
  53. g_driver_log = driver_logger;
  54. }
  55. // convert a string from utf-16 to the encoding and return the new string in the pointer parameter and new
  56. // length in the len parameter. If no errors occurred during convertion, true is returned and the original
  57. // utf-16 string is released by this function if no errors occurred. Otherwise the parameters are not changed
  58. // and false is returned.
  59. bool convert_string_from_utf16( SQLSRV_ENCODING encoding, char** string, SQLINTEGER& len, bool free_utf16 )
  60. {
  61. char* utf16_string = *string;
  62. unsigned int utf16_len = len / 2; // from # of bytes to # of wchars
  63. char *enc_string = NULL;
  64. unsigned int enc_len = 0;
  65. // for the empty string, we simply returned we converted it
  66. if( len == 0 && *string[0] == '\0' ) {
  67. return true;
  68. }
  69. // flags set to 0 by default, which means that any invalid characters are dropped rather than causing
  70. // an error. This happens only on XP.
  71. DWORD flags = 0;
  72. if( encoding == CP_UTF8 && g_osversion.dwMajorVersion >= SQLSRV_OS_VISTA_OR_LATER ) {
  73. // Vista (and later) will detect invalid UTF-16 characters and raise an error.
  74. flags = WC_ERR_INVALID_CHARS;
  75. }
  76. // calculate the number of characters needed
  77. enc_len = WideCharToMultiByte( encoding, flags,
  78. reinterpret_cast<LPCWSTR>( utf16_string ), utf16_len,
  79. NULL, 0, NULL, NULL );
  80. if( enc_len == 0 ) {
  81. return false;
  82. }
  83. // we must allocate a new buffer because it is possible that a UTF-8 string is longer than
  84. // the corresponding UTF-16 string, so we cannot use an inplace conversion
  85. enc_string = reinterpret_cast<char*>( sqlsrv_malloc( enc_len + 1 /* NULL char*/ ));
  86. int rc = WideCharToMultiByte( encoding, flags,
  87. reinterpret_cast<LPCWSTR>( utf16_string ), utf16_len,
  88. enc_string, enc_len, NULL, NULL );
  89. if( rc == 0 ) {
  90. return false;
  91. }
  92. enc_string[ enc_len ] = '\0'; // null terminate the encoded string
  93. if( free_utf16 ) {
  94. sqlsrv_free( utf16_string );
  95. }
  96. *string = enc_string;
  97. len = enc_len;
  98. return true;
  99. }
  100. // thin wrapper around convert_string_from_default_encoding that handles
  101. // allocation of the destination string. An empty string passed in returns
  102. // failure since it's a failure case for convert_string_from_default_encoding.
  103. wchar_t* utf16_string_from_mbcs_string( SQLSRV_ENCODING php_encoding, const char* mbcs_string, unsigned int mbcs_len,
  104. unsigned int* utf16_len )
  105. {
  106. *utf16_len = (mbcs_len + 1);
  107. wchar_t* utf16_string = reinterpret_cast<wchar_t*>( sqlsrv_malloc( *utf16_len * sizeof( wchar_t )));
  108. *utf16_len = convert_string_from_default_encoding( php_encoding, mbcs_string, mbcs_len,
  109. utf16_string, *utf16_len );
  110. if( *utf16_len == 0 ) {
  111. // we preserve the error and reset it because sqlsrv_free resets the last error
  112. DWORD last_error = GetLastError();
  113. sqlsrv_free( utf16_string );
  114. SetLastError( last_error );
  115. return NULL;
  116. }
  117. return utf16_string;
  118. }
  119. // call to retrieve an error from ODBC. This uses SQLGetDiagRec, so the
  120. // errno is 1 based. It returns it as an array with 3 members:
  121. // 1/SQLSTATE) sqlstate
  122. // 2/code) driver specific error code
  123. // 3/message) driver specific error message
  124. // The fetch type determines if the indices are numeric, associative, or both.
  125. bool core_sqlsrv_get_odbc_error( sqlsrv_context& ctx, int record_number, sqlsrv_error_auto_ptr& error, logging_severity severity
  126. TSRMLS_DC )
  127. {
  128. SQLHANDLE h = ctx.handle();
  129. SQLSMALLINT h_type = ctx.handle_type();
  130. if( h == NULL ) {
  131. return false;
  132. }
  133. zval* ssphp_z = NULL;
  134. int zr = SUCCESS;
  135. zval* temp = NULL;
  136. SQLRETURN r = SQL_SUCCESS;
  137. SQLINTEGER sqlstate_len = SQL_SQLSTATE_BUFSIZE * sizeof( wchar_t );
  138. SQLSMALLINT wmessage_len = 0;
  139. SQLINTEGER message_len = 0;
  140. SQLWCHAR wsqlstate[ SQL_SQLSTATE_BUFSIZE ];
  141. SQLWCHAR wnative_message[ SQL_MAX_MESSAGE_LENGTH + 1 ];
  142. SQLSRV_ENCODING enc = ctx.encoding();
  143. switch( h_type ) {
  144. case SQL_HANDLE_STMT:
  145. {
  146. sqlsrv_stmt* stmt = static_cast<sqlsrv_stmt*>( &ctx );
  147. if( stmt->current_results != NULL ) {
  148. error = stmt->current_results->get_diag_rec( record_number );
  149. // don't use the CHECK* macros here since it will trigger reentry into the error handling system
  150. if( error == NULL ) {
  151. return false;
  152. }
  153. break;
  154. }
  155. // convert the error into the encoding of the context
  156. if( enc == SQLSRV_ENCODING_DEFAULT ) {
  157. enc = stmt->conn->encoding();
  158. }
  159. }
  160. default:
  161. error = new ( sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error();
  162. r = SQLGetDiagRecW( h_type, h, record_number, wsqlstate, &error->native_code, wnative_message,
  163. SQL_MAX_MESSAGE_LENGTH + 1, &wmessage_len );
  164. // don't use the CHECK* macros here since it will trigger reentry into the error handling system
  165. if( !SQL_SUCCEEDED( r ) || r == SQL_NO_DATA ) {
  166. return false;
  167. }
  168. error->sqlstate = reinterpret_cast<SQLCHAR*>( wsqlstate );
  169. convert_string_from_utf16( enc, reinterpret_cast<char**>( &error->sqlstate ), sqlstate_len,
  170. false /*no free*/ );
  171. error->native_message = reinterpret_cast<SQLCHAR*>( wnative_message );
  172. message_len = wmessage_len * sizeof( wchar_t );
  173. convert_string_from_utf16( enc, reinterpret_cast<char**>( &error->native_message ), message_len,
  174. false /*no free*/ );
  175. break;
  176. }
  177. // log the error first
  178. LOG( severity, "%1!s!: SQLSTATE = %2!s!", ctx.func(), error->sqlstate );
  179. LOG( severity, "%1!s!: error code = %2!d!", ctx.func(), error->native_code );
  180. LOG( severity, "%1!s!: message = %2!s!", ctx.func(), error->native_message );
  181. error->format = false;
  182. return true;
  183. }
  184. // format and return a driver specfic error
  185. void core_sqlsrv_format_driver_error( sqlsrv_context& ctx, sqlsrv_error_const const* custom_error,
  186. sqlsrv_error_auto_ptr& formatted_error, logging_severity severity TSRMLS_DC, va_list* args )
  187. {
  188. // allocate space for the formatted message
  189. formatted_error = new (sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error();
  190. formatted_error->sqlstate = reinterpret_cast<SQLCHAR*>( sqlsrv_malloc( SQL_SQLSTATE_BUFSIZE ));
  191. formatted_error->native_message = reinterpret_cast<SQLCHAR*>( sqlsrv_malloc( SQL_MAX_MESSAGE_LENGTH + 1 ));
  192. DWORD rc = FormatMessage( FORMAT_MESSAGE_FROM_STRING, reinterpret_cast<LPSTR>( custom_error->native_message ), 0, 0,
  193. reinterpret_cast<LPSTR>( formatted_error->native_message ), SQL_MAX_MESSAGE_LENGTH, args );
  194. if( rc == 0 ) {
  195. strcpy_s( reinterpret_cast<char*>( formatted_error->native_message ), SQL_MAX_MESSAGE_LENGTH,
  196. reinterpret_cast<char*>( INTERNAL_FORMAT_ERROR ));
  197. }
  198. strcpy_s( reinterpret_cast<char*>( formatted_error->sqlstate ), SQL_SQLSTATE_BUFSIZE,
  199. reinterpret_cast<char*>( custom_error->sqlstate ));
  200. formatted_error->native_code = custom_error->native_code;
  201. // log the error
  202. LOG( severity, "%1!s!: SQLSTATE = %2!s!", ctx.func(), formatted_error->sqlstate );
  203. LOG( severity, "%1!s!: error code = %2!d!", ctx.func(), formatted_error->native_code );
  204. LOG( severity, "%1!s!: message = %2!s!", ctx.func(), formatted_error->native_message );
  205. }
  206. DWORD core_sqlsrv_format_message( char* output_buffer, unsigned output_len, const char* format, ... )
  207. {
  208. va_list format_args;
  209. va_start( format_args, format );
  210. DWORD rc = FormatMessage( FORMAT_MESSAGE_FROM_STRING, format, 0, 0, output_buffer, output_len, &format_args );
  211. va_end( format_args );
  212. return rc;
  213. }
  214. // return an error message for GetLastError using FormatMessage.
  215. // this function returns the msg pointer so that it may be used within
  216. // another function call such as handle_error
  217. const char* get_last_error_message( DWORD last_error )
  218. {
  219. if( last_error == 0 ) {
  220. last_error = GetLastError();
  221. }
  222. DWORD r = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, last_error, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
  223. last_err_msg, sizeof( last_err_msg ), NULL );
  224. if( r == 0 ) {
  225. SQLSRV_STATIC_ASSERT( sizeof( INTERNAL_FORMAT_ERROR ) < sizeof( last_err_msg ));
  226. std::copy( INTERNAL_FORMAT_ERROR, INTERNAL_FORMAT_ERROR + sizeof( INTERNAL_FORMAT_ERROR ), last_err_msg );
  227. }
  228. return last_err_msg;
  229. }
  230. // die
  231. // Terminate the PHP request with an error message
  232. // We use this function rather than php_error directly because we use the FormatMessage syntax in most other
  233. // places within the extension (e.g., LOG), whereas php_error uses the printf format syntax. There were
  234. // places where we were using the FormatMessage syntax inadvertently with DIE which left messages without
  235. // proper information. Rather than convert those messages and try and remember the difference between LOG and
  236. // DIE, it is simpler to make the format syntax common between them.
  237. void die( const char* msg, ... )
  238. {
  239. va_list format_args;
  240. va_start( format_args, msg );
  241. DWORD rc = FormatMessage( FORMAT_MESSAGE_FROM_STRING, msg, 0, 0, last_err_msg, sizeof( last_err_msg ), &format_args );
  242. va_end( format_args );
  243. if( rc == 0 ) {
  244. php_error( E_ERROR, reinterpret_cast<const char*>( INTERNAL_FORMAT_ERROR ));
  245. }
  246. php_error( E_ERROR, last_err_msg );
  247. }
  248. namespace {
  249. // convert from the default encoding specified by the "CharacterSet"
  250. // connection option to UTF-16. mbcs_len and utf16_len are sizes in
  251. // bytes. The return is the number of UTF-16 characters in the string
  252. // returned in utf16_out_string. An empty string passed in will result as
  253. // a failure since MBTWC returns 0 for both an empty string and failure
  254. // to convert.
  255. unsigned int convert_string_from_default_encoding( unsigned int php_encoding, __in_bcount(mbcs_len) char const* mbcs_in_string,
  256. unsigned int mbcs_len, __out_ecount(utf16_len) __transfer( mbcs_in_string ) wchar_t* utf16_out_string,
  257. unsigned int utf16_len )
  258. {
  259. unsigned int win_encoding = CP_ACP;
  260. switch( php_encoding ) {
  261. case SQLSRV_ENCODING_CHAR:
  262. win_encoding = CP_ACP;
  263. break;
  264. // this shouldn't ever be set
  265. case SQLSRV_ENCODING_BINARY:
  266. DIE( "Invalid encoding." );
  267. break;
  268. default:
  269. win_encoding = php_encoding;
  270. break;
  271. }
  272. unsigned int required_len = MultiByteToWideChar( win_encoding, MB_ERR_INVALID_CHARS, mbcs_in_string, mbcs_len,
  273. utf16_out_string, utf16_len );
  274. if( required_len == 0 ) {
  275. return 0;
  276. }
  277. utf16_out_string[ required_len ] = '\0';
  278. return required_len;
  279. }
  280. }