PageRenderTime 49ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/sqlsrv/util.cpp

#
C++ | 958 lines | 632 code | 200 blank | 126 comment | 133 complexity | 45fb1eee29761917c7da95968d37f01b MD5 | raw file
  1. //---------------------------------------------------------------------------------------------------------------------------------
  2. // File: util.cpp
  3. //
  4. // Contents: Utility functions used by both connection or statement functions
  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 "php_sqlsrv.h"
  23. #include <windows.h>
  24. namespace {
  25. // current subsytem. defined for the CHECK_SQL_{ERROR|WARNING} macros
  26. unsigned int current_log_subsystem = LOG_UTIL;
  27. // buffer used to hold a formatted log message prior to actually logging it.
  28. const int LOG_MSG_SIZE = 2048;
  29. char log_msg[ LOG_MSG_SIZE ];
  30. // internal error that says that FormatMessage failed
  31. SQLCHAR INTERNAL_FORMAT_ERROR[] = "An internal error occurred. FormatMessage failed writing an error message.";
  32. // *** internal functions ***
  33. void copy_error_to_zval( zval** error_z, sqlsrv_error_const* error, zval** reported_chain, zval** ignored_chain,
  34. bool warning TSRMLS_DC );
  35. bool ignore_warning( char* sql_state, int native_code TSRMLS_DC );
  36. bool handle_errors_and_warnings( sqlsrv_context& ctx, zval** reported_chain, zval** ignored_chain, logging_severity log_severity,
  37. unsigned int sqlsrv_error_code, bool warning, va_list* print_args TSRMLS_DC );
  38. int sqlsrv_merge_zend_hash_dtor( void* dest TSRMLS_DC );
  39. bool sqlsrv_merge_zend_hash( __inout zval* dest_z, zval const* src_z TSRMLS_DC );
  40. }
  41. // List of all error messages
  42. ss_error SS_ERRORS[] = {
  43. {
  44. SS_SQLSRV_ERROR_INVALID_OPTION,
  45. { IMSSP, (SQLCHAR*)"Invalid option %1!s! was passed to sqlsrv_connect.", -1, true }
  46. },
  47. // no equivalent to error 2 in 2.0
  48. // error 3 is superceded by -16
  49. // these two share the same code since they are basically the same error.
  50. {
  51. SQLSRV_ERROR_UID_PWD_BRACES_NOT_ESCAPED,
  52. { IMSSP, (SQLCHAR*) "An unescaped right brace (}) was found in either the user name or password. All right braces must be"
  53. " escaped with another right brace (}}).", -4, false }
  54. },
  55. {
  56. SS_SQLSRV_ERROR_CONNECT_BRACES_NOT_ESCAPED,
  57. { IMSSP, (SQLCHAR*)"An unescaped right brace (}) was found in option %1!s!.", -4, true }
  58. },
  59. {
  60. SQLSRV_ERROR_NO_DATA,
  61. { IMSSP, (SQLCHAR*)"Field %1!d! returned no data.", -5, true }
  62. },
  63. {
  64. SQLSRV_ERROR_STREAMABLE_TYPES_ONLY,
  65. { IMSSP, (SQLCHAR*)"Only char, nchar, varchar, nvarchar, binary, varbinary, and large object types can be read by using "
  66. "streams.", -6, false}
  67. },
  68. {
  69. SS_SQLSRV_ERROR_INVALID_OUTPUT_PARAM_TYPE,
  70. { IMSSP, (SQLCHAR*)"An invalid PHP type was specified as an output parameter. DateTime objects, NULL values, and streams cannot be "
  71. "specified as output parameters.", -7, false }
  72. },
  73. {
  74. SS_SQLSRV_ERROR_INVALID_CONNECTION_KEY,
  75. { IMSSP, (SQLCHAR*)"An invalid connection option key type was received. Option key types must be strings.", -8, false }
  76. },
  77. {
  78. SS_SQLSRV_ERROR_VAR_REQUIRED,
  79. { IMSSP, (SQLCHAR*)"Parameter array %1!d! must have at least one value or variable.", -9, true }
  80. },
  81. {
  82. SS_SQLSRV_ERROR_INVALID_FETCH_TYPE,
  83. { IMSSP, (SQLCHAR*)"An invalid fetch type was specified. SQLSRV_FETCH_NUMERIC, SQLSRV_FETCH_ARRAY and SQLSRV_FETCH_BOTH are acceptable values.", -10, false }
  84. },
  85. {
  86. SQLSRV_ERROR_STATEMENT_NOT_EXECUTED,
  87. { IMSSP, (SQLCHAR*)"The statement must be executed before results can be retrieved.", -11, false }
  88. },
  89. {
  90. SS_SQLSRV_ERROR_ALREADY_IN_TXN,
  91. { IMSSP, (SQLCHAR*)"Cannot begin a transaction until the current transaction has been completed by calling either "
  92. "sqlsrv_commit or sqlsrv_rollback.", -12, false }
  93. },
  94. {
  95. SS_SQLSRV_ERROR_NOT_IN_TXN,
  96. { IMSSP, (SQLCHAR*)"A transaction must be started by calling sqlsrv_begin_transaction before calling sqlsrv_commit or "
  97. "sqlsrv_rollback.", -13, false }
  98. },
  99. {
  100. SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER,
  101. { IMSSP, (SQLCHAR*)"An invalid parameter was passed to %1!s!.", -14, true }
  102. },
  103. {
  104. SS_SQLSRV_ERROR_INVALID_PARAMETER_DIRECTION,
  105. { IMSSP, (SQLCHAR*)"An invalid direction for parameter %1!d! was specified. SQLSRV_PARAM_IN, SQLSRV_PARAM_OUT, and "
  106. "SQLSRV_PARAM_INOUT are valid values.", -15, true }
  107. },
  108. {
  109. SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE,
  110. { IMSSP, (SQLCHAR*)"An invalid PHP type for parameter %1!d! was specified.", -16, true }
  111. },
  112. {
  113. SQLSRV_ERROR_INVALID_PARAMETER_SQLTYPE,
  114. { IMSSP, (SQLCHAR*)"An invalid SQL Server type for parameter %1!d! was specified.", -17, true }
  115. },
  116. {
  117. SQLSRV_ERROR_FETCH_NOT_CALLED,
  118. { IMSSP, (SQLCHAR*)"A row must be retrieved with sqlsrv_fetch before retrieving data with sqlsrv_get_field.", -18, false }
  119. },
  120. {
  121. SQLSRV_ERROR_FIELD_INDEX_ERROR,
  122. { IMSSP, (SQLCHAR*)"Fields within a row must be accessed in ascending order. "
  123. "The sqlsrv_get_field function cannot retrieve field %1!d! because its index is less "
  124. "than the index of a field that has already been retrieved (%2!d!).", -19, true }
  125. },
  126. {
  127. SQLSRV_ERROR_DATETIME_CONVERSION_FAILED,
  128. { IMSSP, (SQLCHAR*)"The retrieval of the DateTime object failed.", -20, false }
  129. },
  130. // no equivalent to SQLSRV_ERROR_SERVER_INFO in 2.0 so -21 is skipped
  131. {
  132. SQLSRV_ERROR_FETCH_PAST_END,
  133. { IMSSP, (SQLCHAR*)"There are no more rows in the active result set. Since this result set is not scrollable, no more "
  134. "data may be retrieved.", -22, false }
  135. },
  136. {
  137. SS_SQLSRV_ERROR_STATEMENT_NOT_PREPARED,
  138. { IMSSP, (SQLCHAR*)"A statement must be prepared with sqlsrv_prepare before calling sqlsrv_execute.", -23, false }
  139. },
  140. {
  141. SQLSRV_ERROR_ZEND_HASH,
  142. { IMSSP, (SQLCHAR*)"An error occurred while creating or accessing a Zend hash table.", -24, false }
  143. },
  144. {
  145. SQLSRV_ERROR_ZEND_STREAM,
  146. { IMSSP, (SQLCHAR*)"An error occurred while reading from a PHP stream.", -25, false }
  147. },
  148. {
  149. SQLSRV_ERROR_NEXT_RESULT_PAST_END,
  150. { IMSSP, (SQLCHAR*)"There are no more results returned by the query.", -26, false }
  151. },
  152. {
  153. SQLSRV_ERROR_STREAM_CREATE,
  154. { IMSSP, (SQLCHAR*)"An error occurred while retrieving a SQL Server field as a stream.", -27, false }
  155. },
  156. {
  157. SQLSRV_ERROR_NO_FIELDS,
  158. { IMSSP, (SQLCHAR*)"The active result for the query contains no fields.", -28, false }
  159. },
  160. {
  161. SS_SQLSRV_ERROR_ZEND_BAD_CLASS,
  162. { IMSSP, (SQLCHAR*)"Failed to find class %1!s!.", -29, true }
  163. },
  164. {
  165. SS_SQLSRV_ERROR_ZEND_OBJECT_FAILED,
  166. { IMSSP, (SQLCHAR*)"Failed to create an instance of class %1!s!.", -30, true }
  167. },
  168. {
  169. SS_SQLSRV_ERROR_INVALID_PARAMETER_PRECISION,
  170. { IMSSP, (SQLCHAR*)"An invalid size or precision for parameter %1!d! was specified.", -31, true }
  171. },
  172. {
  173. SQLSRV_ERROR_INVALID_OPTION_KEY,
  174. { IMSSP, (SQLCHAR*)"Option %1!s! is invalid.", -32, true }
  175. },
  176. // these three errors are returned for invalid options, so they are given the same number for compatibility with 1.1
  177. {
  178. SQLSRV_ERROR_INVALID_QUERY_TIMEOUT_VALUE,
  179. { IMSSP, (SQLCHAR*) "Invalid value %1!s! specified for option SQLSRV_QUERY_TIMEOUT.", -33, true }
  180. },
  181. {
  182. SQLSRV_ERROR_INVALID_OPTION_TYPE_INT,
  183. { IMSSP, (SQLCHAR*) "Invalid value type for option %1!s! was specified. Integer type was expected.", -33, true }
  184. },
  185. {
  186. SQLSRV_ERROR_INVALID_OPTION_TYPE_STRING,
  187. { IMSSP, (SQLCHAR*) "Invalid value type for option %1!s! was specified. String type was expected.", -33, true }
  188. },
  189. {
  190. SQLSRV_ERROR_INPUT_OUTPUT_PARAM_TYPE_MATCH,
  191. { IMSSP, (SQLCHAR*)"The type of output parameter %1!d! does not match the type specified by the SQLSRV_PHPTYPE_* constant."
  192. " For output parameters, the type of the variable's current value must match the SQLSRV_PHPTYPE_* constant, or be NULL. "
  193. "If the type is NULL, the PHP type of the output parameter is inferred from the SQLSRV_SQLTYPE_* constant.", -34, true }
  194. },
  195. {
  196. SQLSRV_ERROR_INVALID_TYPE,
  197. { IMSSP, (SQLCHAR*)"Invalid type", -35, false }
  198. },
  199. // 36-38 have no equivalent 2.0 errors
  200. {
  201. SS_SQLSRV_ERROR_REGISTER_RESOURCE,
  202. { IMSSP, (SQLCHAR*)"Registering the %1!s! resource failed.", -39, true }
  203. },
  204. {
  205. SQLSRV_ERROR_INPUT_PARAM_ENCODING_TRANSLATE,
  206. { IMSSP, (SQLCHAR*)"An error occurred translating string for input param %1!d! to UCS-2: %2!s!", -40, true }
  207. },
  208. {
  209. SQLSRV_ERROR_OUTPUT_PARAM_ENCODING_TRANSLATE,
  210. { IMSSP, (SQLCHAR*)"An error occurred translating string for an output param to UTF-8: %1!s!", -41, true }
  211. },
  212. {
  213. SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE,
  214. { IMSSP, (SQLCHAR*)"An error occurred translating string for a field to UTF-8: %1!s!", -42, true }
  215. },
  216. {
  217. SQLSRV_ERROR_INPUT_STREAM_ENCODING_TRANSLATE,
  218. { IMSSP, (SQLCHAR*)"An error occurred translating a PHP stream from UTF-8 to UTF-16: %1!s!", -43, true }
  219. },
  220. {
  221. SQLSRV_ERROR_MARS_OFF,
  222. { IMSSP, (SQLCHAR*)"The connection cannot process this operation because there is a statement with pending results. "
  223. "To make the connection available for other queries, either fetch all results or cancel or free the statement. "
  224. "For more information, see the product documentation about the MultipleActiveResultSets connection option.", -44, false }
  225. },
  226. {
  227. SQLSRV_ERROR_CONN_OPTS_WRONG_TYPE,
  228. { IMSSP, (SQLCHAR*) "Expected an array of options for the connection. Connection options must be passed as an array of "
  229. "key/value pairs.", -45, false }
  230. },
  231. {
  232. SQLSRV_ERROR_QUERY_STRING_ENCODING_TRANSLATE,
  233. { IMSSP, (SQLCHAR*) "An error occurred translating the query string to UTF-16: %1!s!.", -46, true }
  234. },
  235. {
  236. SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE,
  237. { IMSSP, (SQLCHAR*) "An error occurred translating the connection string to UTF-16: %1!s!", -47, true }
  238. },
  239. {
  240. SS_SQLSRV_ERROR_CONNECT_ILLEGAL_ENCODING,
  241. { IMSSP, (SQLCHAR*)"The encoding '%1!s!' is not a supported encoding for the CharacterSet connection option.", -48, true }
  242. },
  243. {
  244. SQLSRV_ERROR_DRIVER_NOT_INSTALLED,
  245. { IMSSP, (SQLCHAR*) "This extension requires the Microsoft SQL Server 2011 Native Client. "
  246. "Access the following URL to download the Microsoft SQL Server 2011 Native Client ODBC driver for %1!s!: "
  247. "http://go.microsoft.com/fwlink/?LinkId=163712", -49, true }
  248. },
  249. {
  250. SS_SQLSRV_ERROR_STATEMENT_NOT_SCROLLABLE,
  251. { IMSSP, (SQLCHAR*)"This function only works with statements that have static or keyset scrollable cursors.", -50, false }
  252. },
  253. {
  254. SS_SQLSRV_ERROR_STATEMENT_SCROLLABLE,
  255. { IMSSP, (SQLCHAR*)"This function only works with statements that are not scrollable.", -51, false }
  256. },
  257. // new error for 2.0, used here since 1.1 didn't have a -52
  258. {
  259. SQLSRV_ERROR_MAX_PARAMS_EXCEEDED,
  260. { IMSSP, (SQLCHAR*) "Tried to bind parameter number %1!d!. SQL Server supports a maximum of 2100 parameters.", -52, true }
  261. },
  262. {
  263. SS_SQLSRV_ERROR_INVALID_FETCH_STYLE,
  264. { IMSSP, (SQLCHAR*)"The scroll type passed to sqlsrv_fetch, sqlsrv_fetch_array, or sqlsrv_fetch_object was not valid. "
  265. "Please use one of the SQLSRV_SCROLL constants.", -53, false }
  266. },
  267. {
  268. SQLSRV_ERROR_INVALID_OPTION_SCROLLABLE,
  269. { IMSSP, (SQLCHAR*)"The value passed for the 'Scrollable' statement option is invalid. Please use 'static', 'dynamic', "
  270. "'keyset', 'forward', or 'buffered'.", -54, false }
  271. },
  272. {
  273. SQLSRV_ERROR_UNKNOWN_SERVER_VERSION,
  274. { IMSSP, (SQLCHAR*)"Failed to retrieve the server version. Unable to continue.", -55, false }
  275. },
  276. {
  277. SQLSRV_ERROR_INVALID_PARAMETER_ENCODING,
  278. { IMSSP, (SQLCHAR*) "An invalid encoding was specified for parameter %1!d!.", -56, true }
  279. },
  280. {
  281. SS_SQLSRV_ERROR_PARAM_INVALID_INDEX,
  282. { IMSSP, (SQLCHAR*)"String keys are not allowed in parameters arrays.", -57, false }
  283. },
  284. {
  285. SQLSRV_ERROR_OUTPUT_PARAM_TRUNCATED,
  286. { IMSSP, (SQLCHAR*) "String data, right truncated for output parameter %1!d!.", -58, true }
  287. },
  288. {
  289. SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED,
  290. { IMSSP, (SQLCHAR*) "Memory limit of %1!d! KB exceeded for buffered query", -59, true }
  291. },
  292. {
  293. SQLSRV_ERROR_INVALID_BUFFER_LIMIT,
  294. { IMSSP, (SQLCHAR*) "Setting for " INI_BUFFERED_QUERY_LIMIT " was non-int or non-positive.", -60, false }
  295. },
  296. // internal warning definitions
  297. {
  298. SS_SQLSRV_WARNING_FIELD_NAME_EMPTY,
  299. { SSPWARN, (SQLCHAR*)"An empty field name was skipped by sqlsrv_fetch_object.", -100, false }
  300. },
  301. // terminate the list of errors/warnings
  302. { -1, {} }
  303. };
  304. sqlsrv_error_const* get_error_message( unsigned int sqlsrv_error_code ) {
  305. sqlsrv_error_const *error_message = NULL;
  306. int zr = zend_hash_index_find( g_ss_errors_ht, sqlsrv_error_code, reinterpret_cast<void**>( &error_message ));
  307. if( zr == FAILURE ) {
  308. DIE( "get_error_message: zend_hash_index_find returned failure for sqlsrv_error_code = %1!d!", sqlsrv_error_code );
  309. }
  310. SQLSRV_ASSERT( error_message != NULL, "get_error_message: error_message was null");
  311. return error_message;
  312. }
  313. // Formats an error message and finally writes it to the php log.
  314. void ss_sqlsrv_log( unsigned int severity TSRMLS_DC, const char* msg, va_list* print_args )
  315. {
  316. if(( severity & SQLSRV_G( log_severity )) && ( SQLSRV_G( current_subsystem ) & SQLSRV_G( log_subsystems ))) {
  317. DWORD rc = FormatMessage( FORMAT_MESSAGE_FROM_STRING, msg, 0, 0, log_msg, LOG_MSG_SIZE, print_args );
  318. // if an error occurs for FormatMessage, we just output an internal error occurred.
  319. if( rc == 0 ) {
  320. SQLSRV_STATIC_ASSERT( sizeof( INTERNAL_FORMAT_ERROR ) < sizeof( log_msg ));
  321. std::copy( INTERNAL_FORMAT_ERROR, INTERNAL_FORMAT_ERROR + sizeof( INTERNAL_FORMAT_ERROR ), log_msg );
  322. }
  323. php_log_err( log_msg TSRMLS_CC );
  324. }
  325. }
  326. bool ss_error_handler(sqlsrv_context& ctx, unsigned int sqlsrv_error_code, bool warning TSRMLS_DC, va_list* print_args )
  327. {
  328. logging_severity severity = SEV_ERROR;
  329. if( warning && !SQLSRV_G( warnings_return_as_errors )) {
  330. severity = SEV_WARNING;
  331. }
  332. return handle_errors_and_warnings( ctx, &SQLSRV_G( errors ), &SQLSRV_G( warnings ), severity, sqlsrv_error_code, warning,
  333. print_args TSRMLS_CC );
  334. }
  335. // sqlsrv_errors( [int $errorsAndOrWarnings] )
  336. //
  337. // Returns extended error and/or warning information about the last sqlsrv
  338. // operation performed.
  339. //
  340. // The sqlsrv_errors function can return error and/or warning information by
  341. // calling it with one of the following parameter values below.
  342. //
  343. // Parameters
  344. //
  345. // $errorsAndOrWarnings[OPTIONAL]: A predefined constant. This parameter can
  346. // take one of the values listed:
  347. //
  348. // SQLSRV_ERR_ALL
  349. // Errors and warnings generated on the last sqlsrv function call are returned.
  350. // SQLSRV_ERR_ERRORS
  351. // Errors generated on the last sqlsrv function call are returned.
  352. // SQLSRV_ERR_WARNINGS
  353. // Warnings generated on the last sqlsrv function call are returned.
  354. //
  355. // If no parameter value is supplied, SQLSRV_ERR_ALL is the default
  356. //
  357. // Return Value
  358. // An array of arrays, or null. An example of an error returned:
  359. // Array
  360. // (
  361. // [0] => Array
  362. // (
  363. // [0] => HYT00
  364. // [SQLSTATE] => HYT00
  365. // [1] => 0
  366. // [code] => 0
  367. // [2] => [Microsoft][SQL Native Client]Query timeout expired
  368. // [message] => [Microsoft][SQL Native Client]Query timeout expired
  369. // )
  370. // )
  371. PHP_FUNCTION( sqlsrv_errors )
  372. {
  373. SQLSRV_UNUSED( return_value_used );
  374. SQLSRV_UNUSED( this_ptr );
  375. SQLSRV_UNUSED( return_value_ptr );
  376. long flags = SQLSRV_ERR_ALL;
  377. full_mem_check(MEMCHECK_SILENT);
  378. LOG_FUNCTION( "sqlsrv_errors" );
  379. if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flags ) == FAILURE ) {
  380. LOG(SEV_ERROR, "An invalid parameter was passed to %1!s!.", _FN_ );
  381. RETURN_FALSE;
  382. }
  383. if( flags == SQLSRV_ERR_ALL ) {
  384. int result;
  385. zval_auto_ptr both_z;
  386. MAKE_STD_ZVAL( both_z );
  387. result = array_init( both_z );
  388. if( result == FAILURE ) {
  389. RETURN_FALSE;
  390. }
  391. if( Z_TYPE_P( SQLSRV_G( errors )) == IS_ARRAY && !sqlsrv_merge_zend_hash( both_z, SQLSRV_G( errors ) TSRMLS_CC )) {
  392. RETURN_FALSE;
  393. }
  394. if( Z_TYPE_P( SQLSRV_G( warnings )) == IS_ARRAY && !sqlsrv_merge_zend_hash( both_z, SQLSRV_G( warnings ) TSRMLS_CC )) {
  395. RETURN_FALSE;
  396. }
  397. if( zend_hash_num_elements( Z_ARRVAL_P( both_z )) == 0 ) {
  398. RETURN_NULL();
  399. }
  400. zval_ptr_dtor( &return_value );
  401. *return_value_ptr = both_z;
  402. both_z.transferred();
  403. }
  404. else if( flags == SQLSRV_ERR_WARNINGS ) {
  405. zval_ptr_dtor( &return_value );
  406. *return_value_ptr = SQLSRV_G( warnings );
  407. zval_add_ref( &SQLSRV_G( warnings ));
  408. }
  409. else {
  410. zval_ptr_dtor( &return_value );
  411. *return_value_ptr = SQLSRV_G( errors );
  412. zval_add_ref( &SQLSRV_G( errors ));
  413. }
  414. }
  415. // sqlsrv_configure( string $setting, mixed $value )
  416. //
  417. // Changes the settings for error handling and logging options.
  418. //
  419. // Parameters
  420. // $setting: The name of the setting to be configured. The possible implemented values are
  421. // "WarningsReturnAsErrors", "LogSubsystems", and "LogSeverity".
  422. //
  423. // $value: The value to be applied to the setting specified in the $setting
  424. // parameter. See MSDN or the MINIT function for possible values.
  425. //
  426. // Return Value
  427. // If sqlsrv_configure is called with an unsupported setting or value, the
  428. // function returns false. Otherwise, the function returns true.
  429. PHP_FUNCTION( sqlsrv_configure )
  430. {
  431. SQLSRV_UNUSED( return_value_used );
  432. SQLSRV_UNUSED( this_ptr );
  433. SQLSRV_UNUSED( return_value_ptr );
  434. LOG_FUNCTION( "sqlsrv_configure" );
  435. char* option;
  436. int option_len;
  437. zval* value_z;
  438. sqlsrv_context_auto_ptr error_ctx;
  439. RETVAL_FALSE;
  440. reset_errors( TSRMLS_C );
  441. try {
  442. // dummy context to pass onto the error handler
  443. error_ctx = new ( sqlsrv_malloc( sizeof( sqlsrv_context ))) sqlsrv_context( 0, ss_error_handler, NULL );
  444. SET_FUNCTION_NAME( *error_ctx );
  445. int zr = zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "sz", &option, &option_len, &value_z );
  446. CHECK_CUSTOM_ERROR(( zr == FAILURE ), error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ) {
  447. throw ss::SSException();
  448. }
  449. // WarningsReturnAsErrors
  450. if( !stricmp( option, INI_WARNINGS_RETURN_AS_ERRORS )) {
  451. SQLSRV_G( warnings_return_as_errors ) = zend_is_true( value_z ) ? true : false;
  452. LOG( SEV_NOTICE, INI_PREFIX INI_WARNINGS_RETURN_AS_ERRORS " = %1!s!", SQLSRV_G( warnings_return_as_errors ) ? "On" : "Off");
  453. RETURN_TRUE;
  454. }
  455. // LogSeverity
  456. else if( !stricmp( option, INI_LOG_SEVERITY )) {
  457. CHECK_CUSTOM_ERROR(( Z_TYPE_P( value_z ) != IS_LONG ), error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ) {
  458. throw ss::SSException();
  459. }
  460. long severity_mask = Z_LVAL_P( value_z );
  461. // make sure they can't use 0 to shut off the masking in the severity
  462. if( severity_mask < SEV_ALL || severity_mask == 0 || severity_mask > (SEV_NOTICE + SEV_ERROR + SEV_WARNING) ) {
  463. RETURN_FALSE;
  464. }
  465. SQLSRV_G( log_severity ) = static_cast<logging_severity>( severity_mask );
  466. LOG( SEV_NOTICE, INI_PREFIX INI_LOG_SEVERITY " = %1!d!", SQLSRV_G( log_severity ));
  467. RETURN_TRUE;
  468. }
  469. // LogSubsystems
  470. else if( !stricmp( option, INI_LOG_SUBSYSTEMS )) {
  471. CHECK_CUSTOM_ERROR(( Z_TYPE_P( value_z ) != IS_LONG ), error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ) {
  472. throw ss::SSException();
  473. }
  474. long subsystem_mask = Z_LVAL_P( value_z );
  475. if( subsystem_mask < LOG_ALL || subsystem_mask > (LOG_INIT + LOG_CONN + LOG_STMT + LOG_UTIL) ) {
  476. RETURN_FALSE;
  477. }
  478. SQLSRV_G( log_subsystems ) = static_cast<logging_subsystems>( subsystem_mask );
  479. LOG( SEV_NOTICE, INI_PREFIX INI_LOG_SUBSYSTEMS " = %1!d!", SQLSRV_G( log_subsystems ));
  480. RETURN_TRUE;
  481. }
  482. else if( !stricmp( option, INI_BUFFERED_QUERY_LIMIT )) {
  483. CHECK_CUSTOM_ERROR(( Z_TYPE_P( value_z ) != IS_LONG ), error_ctx, SQLSRV_ERROR_INVALID_BUFFER_LIMIT, _FN_ ) {
  484. throw ss::SSException();
  485. }
  486. long buffered_query_limit = Z_LVAL_P( value_z );
  487. CHECK_CUSTOM_ERROR( buffered_query_limit < 0, error_ctx, SQLSRV_ERROR_INVALID_BUFFER_LIMIT, _FN_ ) {
  488. throw ss::SSException();
  489. }
  490. SQLSRV_G( buffered_query_limit ) = buffered_query_limit;
  491. LOG( SEV_NOTICE, INI_PREFIX INI_BUFFERED_QUERY_LIMIT " = %1!d!", SQLSRV_G( buffered_query_limit ));
  492. RETURN_TRUE;
  493. }
  494. else {
  495. THROW_CORE_ERROR( error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ );
  496. }
  497. }
  498. catch( core::CoreException& ) {
  499. RETURN_FALSE;
  500. }
  501. catch( ... ) {
  502. DIE( "sqlsrv_configure: Unknown exception caught." );
  503. }
  504. }
  505. // sqlsrv_get_config( string $setting )
  506. //
  507. // Returns the current value of the specified configuration setting.
  508. //
  509. // Parameters
  510. // $setting: The configuration setting for which the value is returned. For a
  511. // list of configurable settings, see sqlsrv_configure.
  512. //
  513. // Return Value
  514. // The value of the setting specified by the $setting parameter. If an invalid
  515. // setting is specified, false is returned and an error is added to the error
  516. // collection. Because false is a valid value for WarningsReturnAsErrors, to
  517. // really determine if an error occurred, call sqlsrv_errors.
  518. PHP_FUNCTION( sqlsrv_get_config )
  519. {
  520. SQLSRV_UNUSED( return_value_used );
  521. SQLSRV_UNUSED( this_ptr );
  522. SQLSRV_UNUSED( return_value_ptr );
  523. char* option = NULL;
  524. int option_len;
  525. sqlsrv_context_auto_ptr error_ctx;
  526. LOG_FUNCTION( "sqlsrv_get_config" );
  527. reset_errors( TSRMLS_C );
  528. try {
  529. // dummy context to pass onto the error handler
  530. error_ctx = new ( sqlsrv_malloc( sizeof( sqlsrv_context ))) sqlsrv_context( 0, ss_error_handler, NULL );
  531. SET_FUNCTION_NAME( *error_ctx );
  532. int zr = zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s", &option, &option_len );
  533. CHECK_CUSTOM_ERROR(( zr == FAILURE ), error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ) {
  534. throw ss::SSException();
  535. }
  536. if( !stricmp( option, INI_WARNINGS_RETURN_AS_ERRORS )) {
  537. ZVAL_BOOL( return_value, SQLSRV_G( warnings_return_as_errors ));
  538. return;
  539. }
  540. else if( !stricmp( option, INI_LOG_SEVERITY )) {
  541. ZVAL_LONG( return_value, SQLSRV_G( log_severity ));
  542. return;
  543. }
  544. else if( !stricmp( option, INI_LOG_SUBSYSTEMS )) {
  545. ZVAL_LONG( return_value, SQLSRV_G( log_subsystems ));
  546. return;
  547. }
  548. else if( !stricmp( option, INI_BUFFERED_QUERY_LIMIT )) {
  549. ZVAL_LONG( return_value, SQLSRV_G( buffered_query_limit ));
  550. return;
  551. }
  552. else {
  553. THROW_CORE_ERROR( error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ );
  554. }
  555. }
  556. catch( core::CoreException& ) {
  557. RETURN_FALSE;
  558. }
  559. catch( ... ) {
  560. DIE( "sqlsrv_get_config: Unknown exception caught." );
  561. }
  562. }
  563. namespace {
  564. void copy_error_to_zval( zval** error_z, sqlsrv_error_const* error, zval** reported_chain, zval** ignored_chain,
  565. bool warning TSRMLS_DC )
  566. {
  567. MAKE_STD_ZVAL( *error_z );
  568. if( array_init( *error_z ) == FAILURE ) {
  569. DIE( "Fatal error during error processing" );
  570. }
  571. // sqlstate
  572. zval_auto_ptr temp;
  573. MAKE_STD_ZVAL( temp );
  574. ZVAL_STRINGL( temp, reinterpret_cast<char*>( error->sqlstate ), SQL_SQLSTATE_SIZE, 1 );
  575. if( add_next_index_zval( *error_z, temp ) == FAILURE ) {
  576. DIE( "Fatal error during error processing" );
  577. }
  578. if( add_assoc_zval( *error_z, "SQLSTATE", temp ) == FAILURE ) {
  579. DIE( "Fatal error during error processing" );
  580. }
  581. zval_add_ref( &temp );
  582. // native_code
  583. if( add_next_index_long( *error_z, error->native_code ) == FAILURE ) {
  584. DIE( "Fatal error during error processing" );
  585. }
  586. if( add_assoc_long( *error_z, "code", error->native_code ) == FAILURE ) {
  587. DIE( "Fatal error during error processing" );
  588. }
  589. // native_message
  590. MAKE_STD_ZVAL( temp );
  591. ZVAL_STRING( temp, reinterpret_cast<char*>( error->native_message), 1 );
  592. if( add_next_index_zval( *error_z, temp ) == FAILURE ) {
  593. DIE( "Fatal error during error processing" );
  594. }
  595. if( add_assoc_zval( *error_z, "message", temp ) == FAILURE ) {
  596. DIE( "Fatal error during error processing" );
  597. }
  598. zval_add_ref( &temp );
  599. temp.transferred();
  600. // If it is an error or if warning_return_as_errors is true than
  601. // add the error or warning to the reported_chain.
  602. if( !warning || SQLSRV_G( warnings_return_as_errors ) )
  603. {
  604. // if the warning is part of the ignored warning list than
  605. // add to the ignored chain if the ignored chain is not null.
  606. if( warning && ignore_warning( reinterpret_cast<char*>( error->sqlstate ), error->native_code TSRMLS_CC ) &&
  607. ignored_chain != NULL ) {
  608. if( add_next_index_zval( *ignored_chain, *error_z ) == FAILURE ) {
  609. DIE( "Fatal error during error processing" );
  610. }
  611. }
  612. else {
  613. // It is either an error or a warning which should not be ignored.
  614. if( add_next_index_zval( *reported_chain, *error_z ) == FAILURE ) {
  615. DIE( "Fatal error during error processing" );
  616. }
  617. }
  618. }
  619. else
  620. {
  621. // It is a warning with warning_return_as_errors as false, so simply add it to the ignored_chain list
  622. if( ignored_chain != NULL ) {
  623. if( add_next_index_zval( *ignored_chain, *error_z ) == FAILURE ) {
  624. DIE( "Fatal error during error processing" );
  625. }
  626. }
  627. }
  628. }
  629. bool handle_errors_and_warnings( sqlsrv_context& ctx, zval** reported_chain, zval** ignored_chain, logging_severity log_severity,
  630. unsigned int sqlsrv_error_code, bool warning, va_list* print_args TSRMLS_DC )
  631. {
  632. bool result = true;
  633. bool errors_ignored = false;
  634. int prev_reported_cnt = 0;
  635. bool reported_chain_was_null = false;
  636. bool ignored_chain_was_null = false;
  637. int zr = SUCCESS;
  638. zval* error_z = NULL;
  639. sqlsrv_error_auto_ptr error;
  640. // array of reported errors
  641. if( Z_TYPE_P( *reported_chain ) == IS_NULL ) {
  642. reported_chain_was_null = true;
  643. zr = array_init( *reported_chain );
  644. if( zr == FAILURE ) {
  645. DIE( "Fatal error in handle_errors_and_warnings" );
  646. }
  647. }
  648. else {
  649. prev_reported_cnt = zend_hash_num_elements( Z_ARRVAL_PP( reported_chain ));
  650. }
  651. // array of ignored errors
  652. if( ignored_chain != NULL ) {
  653. if( Z_TYPE_P( *ignored_chain ) == IS_NULL ) {
  654. ignored_chain_was_null = true;
  655. zr = array_init( *ignored_chain );
  656. if( zr == FAILURE ) {
  657. DIE( "Fatal error in handle_errors_and_warnings" );
  658. }
  659. }
  660. }
  661. if( sqlsrv_error_code != SQLSRV_ERROR_ODBC ) {
  662. core_sqlsrv_format_driver_error( ctx, get_error_message( sqlsrv_error_code ), error, log_severity TSRMLS_CC, print_args );
  663. copy_error_to_zval( &error_z, error, reported_chain, ignored_chain, warning TSRMLS_CC );
  664. }
  665. SQLSMALLINT record_number = 0;
  666. do {
  667. result = core_sqlsrv_get_odbc_error( ctx, ++record_number, error, log_severity TSRMLS_CC );
  668. if( result ) {
  669. copy_error_to_zval( &error_z, error, reported_chain, ignored_chain, warning TSRMLS_CC );
  670. }
  671. } while( result );
  672. // If it were a warning, we report that warnings where ignored except if warnings_return_as_errors
  673. // was true and we added some warnings to the reported_chain.
  674. if( warning ) {
  675. errors_ignored = true;
  676. if( SQLSRV_G( warnings_return_as_errors ) ) {
  677. if( zend_hash_num_elements( Z_ARRVAL_PP( reported_chain )) > prev_reported_cnt ) {
  678. // We actually added some errors
  679. errors_ignored = false;
  680. }
  681. }
  682. }
  683. // if the error array came in as NULL and didn't have anything added to it, return it as NULL
  684. if( reported_chain_was_null && zend_hash_num_elements( Z_ARRVAL_PP( reported_chain )) == 0 ) {
  685. zend_hash_destroy( Z_ARRVAL_PP( reported_chain ));
  686. FREE_HASHTABLE( Z_ARRVAL_PP( reported_chain ));
  687. ZVAL_NULL( *reported_chain );
  688. }
  689. if( ignored_chain != NULL && ignored_chain_was_null && zend_hash_num_elements( Z_ARRVAL_PP( ignored_chain )) == 0 ) {
  690. zend_hash_destroy( Z_ARRVAL_PP( ignored_chain ));
  691. FREE_HASHTABLE( Z_ARRVAL_PP( ignored_chain ));
  692. ZVAL_NULL( *ignored_chain );
  693. }
  694. // If it was an error instead of a warning than we always return errors_ignored = false.
  695. return errors_ignored;
  696. }
  697. // return whether or not a warning should be ignored or returned as an error if WarningsReturnAsErrors is true
  698. // see RINIT in init.cpp for information about which errors are ignored.
  699. bool ignore_warning( char* sql_state, int native_code TSRMLS_DC )
  700. {
  701. for( zend_hash_internal_pointer_reset( g_ss_warnings_to_ignore_ht );
  702. zend_hash_has_more_elements( g_ss_warnings_to_ignore_ht ) == SUCCESS;
  703. zend_hash_move_forward( g_ss_warnings_to_ignore_ht ) ) {
  704. void* error_v = NULL;
  705. if( zend_hash_get_current_data( g_ss_warnings_to_ignore_ht, (void**) &error_v ) == FAILURE ) {
  706. return false;
  707. }
  708. sqlsrv_error* error = static_cast<sqlsrv_error*>( error_v );
  709. if( !strncmp( reinterpret_cast<char*>( error->sqlstate ), sql_state, SQL_SQLSTATE_SIZE ) &&
  710. ( error->native_code == native_code || error->native_code == -1 )) {
  711. return true;
  712. }
  713. }
  714. return false;
  715. }
  716. int sqlsrv_merge_zend_hash_dtor( void* dest TSRMLS_DC )
  717. {
  718. #if defined(ZTS)
  719. SQLSRV_UNUSED( tsrm_ls );
  720. #endif
  721. zval_ptr_dtor( reinterpret_cast<zval**>( &dest ));
  722. return ZEND_HASH_APPLY_REMOVE;
  723. }
  724. // sqlsrv_merge_zend_hash
  725. // merge a source hash into a dest hash table and return any errors.
  726. bool sqlsrv_merge_zend_hash( __inout zval* dest_z, zval const* src_z TSRMLS_DC )
  727. {
  728. #if defined(ZTS)
  729. SQLSRV_UNUSED( tsrm_ls );
  730. #endif
  731. if( Z_TYPE_P( dest_z ) != IS_ARRAY && Z_TYPE_P( dest_z ) != IS_NULL ) DIE( "dest_z must be an array or null" );
  732. if( Z_TYPE_P( src_z ) != IS_ARRAY && Z_TYPE_P( src_z ) != IS_NULL ) DIE( "src_z must be an array or null" );
  733. if( Z_TYPE_P( src_z ) == IS_NULL ) {
  734. return true;
  735. }
  736. HashTable* src_ht = Z_ARRVAL_P( src_z );
  737. int result = SUCCESS;
  738. for( zend_hash_internal_pointer_reset( src_ht );
  739. zend_hash_has_more_elements( src_ht ) == SUCCESS;
  740. zend_hash_move_forward( src_ht ) ) {
  741. void* value_v;
  742. zval* value_z;
  743. result = zend_hash_get_current_data( src_ht, (void**) &value_v );
  744. if( result == FAILURE ) {
  745. zend_hash_apply( Z_ARRVAL_P( dest_z ), sqlsrv_merge_zend_hash_dtor TSRMLS_CC );
  746. return false;
  747. }
  748. value_z = *(static_cast<zval**>( value_v ));
  749. result = add_next_index_zval( dest_z, value_z );
  750. if( result == FAILURE ) {
  751. zend_hash_apply( Z_ARRVAL_P( dest_z ), sqlsrv_merge_zend_hash_dtor TSRMLS_CC );
  752. return false;
  753. }
  754. zval_add_ref( &value_z );
  755. }
  756. return true;
  757. }
  758. } // namespace