2008-07-28 23:49:47 +02:00
//----------------------------------------------------------------------------------------------------------------------------------
// File: util.cpp
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Contents: Utility functions for the SQL Server 2005 Driver for PHP 1.0
//
// Comments: Mostly error handling and some type handling
//
// License: This software is released under the Microsoft Public License. A copy of the license agreement
// may be found online at http://www.codeplex.com/SQL2K5PHP/license.
//----------------------------------------------------------------------------------------------------------------------------------
# include "php_sqlsrv.h"
# include <windows.h>
namespace {
// *** internal constants ***
// current subsytem. defined for the CHECK_SQL_{ERROR|WARNING} macros
int current_log_subsystem = LOG_UTIL ;
// SQLSTATE for all internal errors
const char IMSSP [ ] = " IMSSP " ;
// SQLSTATE for all internal warnings
const char SSPWARN [ ] = " 01SSP " ;
// buffer used to hold a formatted log message prior to actually logging it.
const int LOG_MSG_SIZE = 2048 ;
char log_msg [ LOG_MSG_SIZE ] ;
// internal error that says that FormatMessage failed
const char * internal_format_error = " An internal error occurred. FormatMessage failed writing an error message. " ;
// *** internal functions ***
bool handle_errors_and_warnings ( sqlsrv_context const * ctx , zval * * reported_chain , zval * * ignored_chain , int log_severity , int log_subsystem ,
const char * _FN_ , sqlsrv_error const * ssphp , va_list args TSRMLS_DC ) ;
bool ignore_warning ( char const * sql_state , int native_code TSRMLS_DC ) ;
bool sqlsrv_merge_zend_hash ( __inout zval * dest_z , zval const * src_z TSRMLS_DC ) ;
int sqlsrv_merge_zend_hash_dtor ( void * dest TSRMLS_DC ) ;
}
// internal error defintions. see sqlsrv_error structure definition in php_sqlsrv.h for more information
sqlsrv_error SQLSRV_ERROR_INVALID_OPTION [ ] = {
{ IMSSP , " Invalid option %1!s! was passed to sqlsrv_connect. " , - 1 , true }
} ;
sqlsrv_error SQLSRV_ERROR_FILE_VERSION [ ] = {
{ IMSSP , " An error occurred when retrieving the extension version. " , - 2 , false }
} ;
sqlsrv_error SQLSRV_ERROR_INVALID_PARAM_TYPE [ ] = {
{ IMSSP , " An unknown type for a bound parameter was specified. " , - 3 , false }
} ;
sqlsrv_error SQLSRV_ERROR_CONNECT_BRACES_NOT_ESCAPED [ ] = {
{ IMSSP , " An unescaped right brace (}) was found in option %1!s!. " , - 4 , true }
} ;
sqlsrv_error SQLSRV_ERROR_NO_DATA [ ] = {
{ IMSSP , " Field %1!d! returned no data. " , - 5 , true }
} ;
sqlsrv_error SQLSRV_ERROR_STREAMABLE_TYPES_ONLY [ ] = {
{ IMSSP , " Only char, nchar, varchar, nvarchar, binary, varbinary, and large object types can be read by using streams. " , - 6 , false }
} ;
sqlsrv_error SQLSRV_ERROR_INVALID_OUTPUT_PARAM_TYPE [ ] = {
{ IMSSP , " An invalid PHP type was specified as an output parameter. DateTime objects, NULL values, and streams cannot be specified as output parameters. " , - 7 , false }
} ;
sqlsrv_error SQLSRV_ERROR_INVALID_CONNECTION_KEY [ ] = {
{ IMSSP , " An invalid connection option key type was received. Option key types must be strings. " , - 8 , false }
} ;
sqlsrv_error SQLSRV_ERROR_VAR_REQUIRED [ ] = {
{ IMSSP , " Parameter array %1!d! must have at least one value or variable. " , - 9 , true }
} ;
sqlsrv_error SQLSRV_ERROR_INVALID_FETCH_TYPE [ ] = {
{ IMSSP , " An invalid fetch type was specified. SQLSRV_FETCH_NUMERIC, SQLSRV_FETCH_ARRAY, and SQLSRV_FETCH_BOTH are acceptable values. " , - 10 , false }
} ;
sqlsrv_error SQLSRV_ERROR_STATEMENT_NOT_EXECUTED [ ] = {
{ IMSSP , " The statement must be executed before results can be retrieved. " , - 11 , false }
} ;
sqlsrv_error SQLSRV_ERROR_ALREADY_IN_TXN [ ] = {
{ IMSSP , " Cannot begin a transaction until the current transaction has been completed by calling either sqlsrv_commit or sqlsrv_rollback. " , - 12 , false }
} ;
sqlsrv_error SQLSRV_ERROR_NOT_IN_TXN [ ] = {
{ IMSSP , " A transaction must be started by calling sqlsrv_begin_transaction before calling sqlsrv_commit or sqlsrv_rollback. " , - 13 , false }
} ;
sqlsrv_error SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER [ ] = {
{ IMSSP , " An invalid parameter was passed to %1!s!. " , - 14 , true }
} ;
sqlsrv_error SQLSRV_ERROR_INVALID_PARAMETER_DIRECTION [ ] = {
{ IMSSP , " An invalid direction for parameter %1!d! was specified. SQLSRV_PARAM_IN, SQLSRV_PARAM_OUT, and SQLSRV_PARAM_INOUT are valid values. " , - 15 , true }
} ;
sqlsrv_error SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE [ ] = {
{ IMSSP , " An invalid PHP type for parameter %1!d! was specified. " , - 16 , true }
} ;
sqlsrv_error SQLSRV_ERROR_INVALID_PARAMETER_SQLTYPE [ ] = {
{ IMSSP , " An invalid SQL Server type for parameter %1!d! was specified. " , - 17 , true }
} ;
sqlsrv_error SQLSRV_ERROR_FETCH_NOT_CALLED [ ] = {
{ IMSSP , " A row must be retrieved with sqlsrv_fetch before retrieving data with sqlsrv_get_field. " , - 18 , false }
} ;
sqlsrv_error SQLSRV_ERROR_FIELD_INDEX_ERROR [ ] = {
{ IMSSP , " Fields within a row must be accessed in sequential order. The sqlsrv_get_field function cannot retrieve field %1!d! because its index is less than the index of a field that has already been retrieved (%2!d!). " , - 19 , true }
} ;
sqlsrv_error SQLSRV_ERROR_DATETIME_CONVERSION_FAILED [ ] = {
{ IMSSP , " The retrieval of the DateTime object failed. " , - 20 , false }
} ;
sqlsrv_error SQLSRV_ERROR_SERVER_INFO [ ] = {
{ IMSSP , " An error occurred while retrieving the server information. " , - 21 , false }
} ;
sqlsrv_error SQLSRV_ERROR_FETCH_PAST_END [ ] = {
{ IMSSP , " There are no more rows in the active result set. " , - 22 , false }
} ;
sqlsrv_error SQLSRV_ERROR_STATEMENT_NOT_PREPARED [ ] = {
{ IMSSP , " A statement must be prepared with sqlsrv_prepare before calling sqlsrv_execute. " , - 23 , false }
} ;
sqlsrv_error SQLSRV_ERROR_ZEND_HASH [ ] = {
{ IMSSP , " An error occurred while creating or accessing a Zend hash table. " , - 24 , false }
} ;
sqlsrv_error SQLSRV_ERROR_ZEND_STREAM [ ] = {
{ IMSSP , " An error occurred while reading from a PHP stream. " , - 25 , false }
} ;
sqlsrv_error SQLSRV_ERROR_NEXT_RESULT_PAST_END [ ] = {
{ IMSSP , " There are no more results returned by the query. " , - 26 , false }
} ;
sqlsrv_error SQLSRV_ERROR_STREAM_CREATE [ ] = {
{ IMSSP , " An error occurred while retrieving a SQL Server field as a stream. " , - 27 , false }
} ;
sqlsrv_error SQLSRV_ERROR_NO_FIELDS [ ] = {
{ IMSSP , " The active result for the query contains no fields. " , - 28 , false }
} ;
sqlsrv_error SQLSRV_ERROR_ZEND_BAD_CLASS [ ] = {
{ IMSSP , " Failed to find class %1!s!. " , - 29 , true }
} ;
sqlsrv_error SQLSRV_ERROR_ZEND_OBJECT_FAILED [ ] = {
{ IMSSP , " Failed to create an instance of class %1!s!. " , - 30 , true }
} ;
sqlsrv_error SQLSRV_ERROR_INVALID_PARAMETER_PRECISION [ ] = {
{ IMSSP , " An invalid size or precision for parameter %1!d! was specified. " , - 31 , true }
} ;
sqlsrv_error SQLSRV_ERROR_INVALID_OPTION_KEY [ ] = {
{ IMSSP , " Option %1!s! is invalid. " , - 32 , true }
} ;
sqlsrv_error SQLSRV_ERROR_INVALID_OPTION_VALUE [ ] = {
{ IMSSP , " Invalid value %1!s! for option %2!s! was specified. " , - 33 , true }
} ;
sqlsrv_error SQLSRV_ERROR_OUTPUT_PARAM_TYPE_DOESNT_MATCH [ ] = {
{ IMSSP , " The type of output parameter %1!d! does not match the type specified by the SQLSRV_PHPTYPE_* constant. "
" For output parameters, the type of the variable's current value must match the SQLSRV_PHPTYPE_* constant, or be NULL. "
" If the type is NULL, the PHP type of the output parameter is inferred from the SQLSRV_SQLTYPE_* constant. " , - 34 , true }
} ;
sqlsrv_error SQLSRV_ERROR_INVALID_TYPE [ ] = {
{ IMSSP , " Invalid type " , - 35 , false }
} ;
sqlsrv_error SQLSRV_ERROR_COMMIT_FAILED [ ] = {
{ IMSSP , " Transaction commit failed. Auto commit mode is still off. " , - 36 , false }
} ;
sqlsrv_error SQLSRV_ERROR_ROLLBACK_FAILED [ ] = {
{ IMSSP , " Transaction rollback failed. Auto commit mode is still off. " , - 37 , false }
} ;
sqlsrv_error SQLSRV_ERROR_AUTO_COMMIT_STILL_OFF [ ] = {
{ IMSSP , " The transaction completed (it was either committed or rolled back). Auto commit mode is still off. " , - 38 , false }
} ;
sqlsrv_error SQLSRV_ERROR_REGISTER_RESOURCE [ ] = {
{ IMSSP , " Registering the %1!s! resource failed. " , - 39 , true }
} ;
// internal warning definitions
sqlsrv_error SQLSRV_WARNING_FIELD_NAME_EMPTY [ ] = {
{ SSPWARN , " An empty field name was skipped by sqlsrv_fetch_object. " , - 100 , false }
} ;
// sqlsrv_errors( [int $errorsAndOrWarnings] )
//
// Returns extended error and/or warning information about the last sqlsrv
// operation performed.
//
// The sqlsrv_errors function can return error and/or warning information by
// calling it with one of the following parameter values below.
//
// Parameters
//
// $errorsAndOrWarnings[OPTIONAL]: A predefined constant. This parameter can
// take one of the values listed:
//
// SQLSRV_ERR_ALL
// Errors and warnings generated on the last sqlsrv function call are returned.
// SQLSRV_ERR_ERRORS
// Errors generated on the last sqlsrv function call are returned.
// SQLSRV_ERR_WARNINGS
// Warnings generated on the last sqlsrv function call are returned.
//
// If no parameter value is supplied, SQLSRV_ERR_ALL is the default
//
// Return Value
// An array of arrays, or null. An example of an error returned:
// Array
// (
// [0] => Array
// (
// [0] => HYT00
// [SQLSTATE] => HYT00
// [1] => 0
// [code] => 0
// [2] => [Microsoft][SQL Native Client]Query timeout expired
// [message] => [Microsoft][SQL Native Client]Query timeout expired
// )
// )
PHP_FUNCTION ( sqlsrv_errors )
{
SQLSRV_UNUSED ( return_value_used ) ;
SQLSRV_UNUSED ( this_ptr ) ;
SQLSRV_UNUSED ( return_value_ptr ) ;
long flags = SQLSRV_ERR_ALL ;
full_mem_check ( MEMCHECK_SILENT ) ;
DECL_FUNC_NAME ( " sqlsrv_errors " ) ;
LOG_FUNCTION ;
if ( zend_parse_parameters ( ZEND_NUM_ARGS ( ) TSRMLS_CC , " |l " , & flags ) = = FAILURE ) {
RETURN_FALSE ;
}
if ( flags = = SQLSRV_ERR_ALL ) {
int result ;
zval_auto_ptr both_z ;
MAKE_STD_ZVAL ( both_z ) ;
result = array_init ( both_z ) ;
if ( result = = FAILURE ) {
zval_ptr_dtor ( & both_z ) ;
RETURN_FALSE ;
}
both_z - > is_ref = 1 ;
if ( Z_TYPE_P ( SQLSRV_G ( errors ) ) = = IS_ARRAY & & ! sqlsrv_merge_zend_hash ( both_z , SQLSRV_G ( errors ) TSRMLS_CC ) ) {
zend_hash_destroy ( Z_ARRVAL_P ( both_z ) ) ;
RETURN_FALSE ;
}
2008-10-16 23:59:02 +02:00
2008-07-28 23:49:47 +02:00
if ( Z_TYPE_P ( SQLSRV_G ( warnings ) ) = = IS_ARRAY & & ! sqlsrv_merge_zend_hash ( both_z , SQLSRV_G ( warnings ) TSRMLS_CC ) ) {
zend_hash_destroy ( Z_ARRVAL_P ( both_z ) ) ;
RETURN_FALSE ;
}
if ( zend_hash_num_elements ( Z_ARRVAL_P ( both_z ) ) = = 0 ) {
RETURN_NULL ( ) ;
}
zval_ptr_dtor ( & return_value ) ;
* return_value_ptr = both_z ;
both_z . transferred ( ) ;
}
else if ( flags = = SQLSRV_ERR_WARNINGS ) {
zval_ptr_dtor ( & return_value ) ;
* return_value_ptr = SQLSRV_G ( warnings ) ;
zval_add_ref ( & SQLSRV_G ( warnings ) ) ;
}
else {
zval_ptr_dtor ( & return_value ) ;
* return_value_ptr = SQLSRV_G ( errors ) ;
zval_add_ref ( & SQLSRV_G ( errors ) ) ;
}
}
// sqlsrv_configure( string $setting, mixed $value )
//
// Changes the settings for error handling and logging options.
//
// Parameters
// $setting: The name of the setting to be configured. The possible implemented values are
// "WarningsReturnAsErrors", "LogSubsystems", and "LogSeverity".
//
// $value: The value to be applied to the setting specified in the $setting
// parameter. See MSDN or the MINIT function for possible values.
//
// Return Value
// If sqlsrv_configure is called with an unsupported setting or value, the
// function returns false. Otherwise, the function returns true.
PHP_FUNCTION ( sqlsrv_configure )
{
SQLSRV_UNUSED ( return_value_used ) ;
SQLSRV_UNUSED ( this_ptr ) ;
SQLSRV_UNUSED ( return_value_ptr ) ;
char * option ;
int option_len ;
zval * value_z ;
DECL_FUNC_NAME ( " sqlsrv_configure " ) ;
LOG_FUNCTION ;
RETVAL_FALSE ;
reset_errors ( TSRMLS_C ) ;
if ( zend_parse_parameters ( ZEND_NUM_ARGS ( ) TSRMLS_CC , " sz " , & option , & option_len , & value_z ) = = FAILURE ) {
handle_error ( NULL , LOG_UTIL , _FN_ , SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER TSRMLS_CC , _FN_ ) ;
RETURN_FALSE ;
}
if ( ! stricmp ( option , INI_WARNINGS_RETURN_AS_ERRORS ) ) {
if ( zend_is_true ( value_z ) ) {
SQLSRV_G ( warnings_return_as_errors ) = true ;
}
else {
SQLSRV_G ( warnings_return_as_errors ) = false ;
}
LOG ( SEV_NOTICE , LOG_UTIL , INI_PREFIX INI_WARNINGS_RETURN_AS_ERRORS " = %1!s! " , SQLSRV_G ( warnings_return_as_errors ) ? " On " : " Off " ) ;
RETURN_TRUE ;
}
else if ( ! stricmp ( option , INI_LOG_SEVERITY ) ) {
if ( Z_TYPE_P ( value_z ) ! = IS_LONG ) {
handle_error ( NULL , LOG_UTIL , _FN_ , SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER TSRMLS_CC , _FN_ ) ;
RETURN_FALSE ;
}
long severity_mask = Z_LVAL_P ( value_z ) ;
// make sure they can't use 0 to shut off the masking in the severity
if ( severity_mask < SEV_ALL | | severity_mask = = 0 | | severity_mask > ( SEV_NOTICE + SEV_ERROR + SEV_WARNING ) ) {
RETURN_FALSE ;
}
SQLSRV_G ( log_severity ) = severity_mask ;
LOG ( SEV_NOTICE , LOG_UTIL , INI_PREFIX INI_LOG_SEVERITY " = %1!d! " , SQLSRV_G ( log_severity ) ) ;
RETURN_TRUE ;
}
else if ( ! stricmp ( option , INI_LOG_SUBSYSTEMS ) ) {
if ( Z_TYPE_P ( value_z ) ! = IS_LONG ) {
handle_error ( NULL , LOG_UTIL , _FN_ , SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER TSRMLS_CC , _FN_ ) ;
RETURN_FALSE ;
}
long subsystem_mask = Z_LVAL_P ( value_z ) ;
LOG ( SEV_NOTICE , LOG_UTIL , " subsystem_mask = %1!d! " , subsystem_mask ) ;
if ( subsystem_mask < LOG_ALL | | subsystem_mask > ( LOG_INIT + LOG_CONN + LOG_STMT + LOG_UTIL ) ) {
RETURN_FALSE ;
}
SQLSRV_G ( log_subsystems ) = subsystem_mask ;
LOG ( SEV_NOTICE , LOG_UTIL , INI_PREFIX INI_LOG_SUBSYSTEMS " = %1!d! " , SQLSRV_G ( log_subsystems ) ) ;
RETURN_TRUE ;
}
else {
LOG ( SEV_ERROR , LOG_UTIL , " Invalid option given to sqlsrv_configure " ) ;
handle_error ( NULL , LOG_UTIL , _FN_ , SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER TSRMLS_CC , _FN_ ) ;
RETURN_FALSE ;
}
}
// sqlsrv_get_config( string $setting )
//
// Returns the current value of the specified configuration setting.
//
// Parameters
// $setting: The configuration setting for which the value is returned. For a
// list of configurable settings, see sqlsrv_configure.
//
// Return Value
// The value of the setting specified by the $setting parameter. If an invalid
// setting is specified, false is returned and an error is added to the error
// collection. Because false is a valid value for WarningsReturnAsErrors, to
// really determine if an error occurred, call sqlsrv_errors.
PHP_FUNCTION ( sqlsrv_get_config )
{
SQLSRV_UNUSED ( return_value_used ) ;
SQLSRV_UNUSED ( this_ptr ) ;
SQLSRV_UNUSED ( return_value_ptr ) ;
char * option ;
int option_len ;
DECL_FUNC_NAME ( " sqlsrv_get_config " ) ;
LOG_FUNCTION ;
reset_errors ( TSRMLS_C ) ;
if ( zend_parse_parameters ( ZEND_NUM_ARGS ( ) TSRMLS_CC , " s " , & option , & option_len ) = = FAILURE ) {
handle_error ( NULL , LOG_UTIL , _FN_ , SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER TSRMLS_CC , _FN_ ) ;
RETURN_FALSE ;
}
if ( ! stricmp ( option , INI_WARNINGS_RETURN_AS_ERRORS ) ) {
ZVAL_BOOL ( return_value , SQLSRV_G ( warnings_return_as_errors ) ) ;
return ;
}
else if ( ! stricmp ( option , INI_LOG_SEVERITY ) ) {
ZVAL_LONG ( return_value , SQLSRV_G ( log_severity ) ) ;
return ;
}
else if ( ! stricmp ( option , INI_LOG_SUBSYSTEMS ) ) {
ZVAL_LONG ( return_value , SQLSRV_G ( log_subsystems ) ) ;
return ;
}
else {
LOG ( SEV_ERROR , LOG_UTIL , " Invalid option given to sqlsrv_get_config. " ) ;
handle_error ( NULL , LOG_UTIL , _FN_ , SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER TSRMLS_CC , _FN_ ) ;
RETURN_FALSE ;
}
}
// wrapper around handle_error that checks a condition and returns whether or not the error was ignored
bool check_sql_error_ex ( bool condition , sqlsrv_context const * ctx , int log_subsystem , const char * _FN_ , sqlsrv_error const * ssphp TSRMLS_DC , . . . )
{
if ( condition ) {
va_list args ;
# if defined(ZTS)
va_start ( args , TSRMLS_C ) ;
# else
va_start ( args , ssphp ) ;
# endif
bool ignored = handle_error ( ctx , log_subsystem , _FN_ , ssphp TSRMLS_CC , args ) ;
if ( ignored ) {
// this will print immediately after the error information in the log
LOG ( SEV_ERROR , log_subsystem , " error ignored " ) ;
}
va_end ( args ) ;
return ignored ;
}
return true ;
}
// this is a special function for sqlsrv internal warnings. It emits an internal warning and treats
// it as an error if the WarningsReturnAsErrors flag is set.
bool check_sqlsrv_warnings ( bool condition , sqlsrv_context const * ctx , int log_subsystem , const char * _FN_ , sqlsrv_error const * ssphp TSRMLS_DC , . . . )
{
// we have to have warning as there is no ODBC error or warning
assert ( ssphp ! = NULL ) ;
if ( condition ) {
va_list args ;
# if defined(ZTS)
va_start ( args , TSRMLS_C ) ;
# else
va_start ( args , ssphp ) ;
# endif
if ( SQLSRV_G ( warnings_return_as_errors ) ) {
bool ignored = handle_error ( ctx , log_subsystem , _FN_ , ssphp TSRMLS_CC , args ) ;
if ( ignored ) {
// this will print immediately after the error information in the log
LOG ( SEV_ERROR , log_subsystem , " error ignored " ) ;
}
va_end ( args ) ;
return ignored ;
}
else {
handle_warning ( ctx , log_subsystem , _FN_ , ssphp TSRMLS_CC , args ) ;
}
va_end ( args ) ;
}
return true ;
}
// wrapper for errors around the common handle_errors_and_warnings
bool handle_error ( sqlsrv_context const * ctx , int log_subsystem , const char * _FN_ , sqlsrv_error const * ssphp TSRMLS_DC , . . . )
{
va_list args ;
# if defined(ZTS)
va_start ( args , TSRMLS_C ) ;
# else
va_start ( args , ssphp ) ;
# endif
LOG ( SEV_NOTICE , LOG_UTIL , " handle_error: entered for function %1!s! " , _FN_ ) ;
// put errors (including warnings treated as errors) into errors and ignored
// warnings into warnings
bool ignored = handle_errors_and_warnings ( ctx , & SQLSRV_G ( errors ) , & SQLSRV_G ( warnings ) , SEV_ERROR , log_subsystem , _FN_ ,
ssphp , args TSRMLS_CC ) ;
va_end ( args ) ;
return ignored ;
}
// wrapper for warnings around the common handle_errors_and_warnings
void handle_warning ( sqlsrv_context const * ctx , int log_subsystem , const char * _FN_ , sqlsrv_error const * ssphp TSRMLS_DC , . . . )
{
va_list args ;
# if defined(ZTS)
va_start ( args , TSRMLS_C ) ;
# else
va_start ( args , ssphp ) ;
# endif
LOG ( SEV_NOTICE , LOG_UTIL , " handle_warning: entered for function %1!s! " , _FN_ ) ;
// put all warnings into the warnings hash table and don't ignore any. No warnings here are treated as errors.
handle_errors_and_warnings ( ctx , & SQLSRV_G ( warnings ) , NULL , SEV_WARNING , log_subsystem , _FN_ ,
ssphp , args TSRMLS_CC ) ;
va_end ( args ) ;
}
// write to the php log if the severity and subsystem match the filters currently set in the INI or
// the script (sqlsrv_configure).
void write_to_log ( unsigned int severity , unsigned int subsystem TSRMLS_DC , const char * msg , . . . )
{
va_list args ;
va_start ( args , msg ) ;
if ( ( severity & SQLSRV_G ( log_severity ) ) & & ( subsystem & SQLSRV_G ( log_subsystems ) ) ) {
DWORD rc = FormatMessage ( FORMAT_MESSAGE_FROM_STRING , msg , 0 , 0 , log_msg , LOG_MSG_SIZE , & args ) ;
// if an error occurs for FormatMessage, we just output an internal error occurred.
if ( rc = = 0 ) {
SQLSRV_STATIC_ASSERT ( sizeof ( internal_format_error ) < sizeof ( log_msg ) ) ;
std : : copy ( internal_format_error , internal_format_error + sizeof ( internal_format_error ) , log_msg ) ;
}
php_log_err ( log_msg TSRMLS_CC ) ;
}
va_end ( args ) ;
}
// *** internal function implementations ***
namespace {
// there are actually two error arrays (potentially) constructed by this function.
// The reported chain is that set of diagnostics that is not in the list of warnings to not report as errors. In other words, it's
// the list of normally processed diagnostics.
// The ignored chain is that set of diagnostics which were specifically ignored and not reported. It is possible for the caller
// to specify no ignored chain by setting the parameter to NULL.
bool handle_errors_and_warnings ( sqlsrv_context const * ctx , zval * * reported_chain , zval * * ignored_chain , int log_severity , int log_subsystem ,
const char * _FN_ , sqlsrv_error const * ssphp , va_list args TSRMLS_DC )
{
zval * ssphp_z = NULL ;
SQLSMALLINT record_number = 1 ;
SQLCHAR sql_state [ 6 ] ;
SQLCHAR message_text [ SQL_MAX_MESSAGE_LENGTH + 1 ] ;
SQLINTEGER native_error = 0 ;
SQLSMALLINT message_len = 0 ;
SQLRETURN r = SQL_SUCCESS ;
zval * temp = NULL ;
bool reported_chain_was_null = false ;
bool ignored_chain_was_null = false ;
int zr = SUCCESS ;
int reported_before = 0 ;
int ignored_before = 0 ;
2008-10-16 23:59:02 +02:00
2008-07-28 23:49:47 +02:00
LOG ( SEV_NOTICE , LOG_UTIL , " handle_errors_and_warnings: entering " ) ;
// create an array of arrays
if ( Z_TYPE_P ( * reported_chain ) = = IS_NULL ) {
reported_chain_was_null = true ;
reported_before = 0 ;
zr = array_init ( * reported_chain ) ;
if ( zr = = FAILURE ) {
DIE ( " Fatal error in handle_errors_and_warnings " ) ;
}
}
else {
reported_before = zend_hash_num_elements ( Z_ARRVAL_PP ( reported_chain ) ) ;
}
if ( ignored_chain ! = NULL ) {
if ( Z_TYPE_P ( * ignored_chain ) = = IS_NULL ) {
2008-10-16 23:59:02 +02:00
ignored_chain_was_null = true ;
2008-07-28 23:49:47 +02:00
ignored_before = 0 ;
2008-10-16 23:59:02 +02:00
zr = array_init ( * ignored_chain ) ;
if ( zr = = FAILURE ) {
DIE ( " Fatal error in handle_errors_and_warnings " ) ;
}
2008-07-28 23:49:47 +02:00
}
else {
ignored_before = zend_hash_num_elements ( Z_ARRVAL_PP ( ignored_chain ) ) ;
}
}
else {
ignored_before = 0 ;
}
// add the PHP error first if there is one.
// We use a while loop to allow the break to exit the loop and avoid having to use a goto for error handling
// the break at the end of the loop assures that we don't get stuck here.
while ( ssphp ) {
emalloc_auto_ptr < sqlsrv_error > ssphp_new ;
emalloc_auto_ptr < const char > ssphp_new_message ;
if ( ssphp - > format ) {
ssphp_new = static_cast < sqlsrv_error * > ( emalloc ( sizeof ( sqlsrv_error ) ) ) ;
ssphp_new - > native_message = ssphp_new_message = static_cast < char const * > ( emalloc ( SQL_MAX_MESSAGE_LENGTH ) ) ;
ssphp_new - > sqlstate = ssphp - > sqlstate ;
ssphp_new - > native_code = ssphp - > native_code ;
DWORD rc = FormatMessage ( FORMAT_MESSAGE_FROM_STRING , const_cast < LPSTR > ( ssphp - > native_message ) , 0 , 0 ,
const_cast < LPSTR > ( ssphp_new - > native_message ) , SQL_MAX_MESSAGE_LENGTH , & args ) ;
if ( rc = = 0 ) {
ssphp_new - > native_message = internal_format_error ;
}
ssphp = ssphp_new ;
}
// log the error first in case of failures below
LOG ( log_severity , log_subsystem , " %1!s!: SQLSTATE = %2!s! " , _FN_ , ssphp - > sqlstate ) ;
LOG ( log_severity , log_subsystem , " %1!s!: error code = %2!d! " , _FN_ , ssphp - > native_code ) ;
LOG ( log_severity , log_subsystem , " %1!s!: message = %2!s! " , _FN_ , ssphp - > native_message ) ;
MAKE_STD_ZVAL ( ssphp_z ) ;
zr = array_init ( ssphp_z ) ;
if ( zr = = FAILURE ) {
zval_ptr_dtor ( & ssphp_z ) ;
break ;
}
// add the error info to the array
MAKE_STD_ZVAL ( temp ) ;
ZVAL_STRINGL ( temp , const_cast < char * > ( ssphp - > sqlstate ) , SQL_SQLSTATE_SIZE , 1 ) ;
zr = add_next_index_zval ( ssphp_z , temp ) ;
if ( zr = = FAILURE ) {
zval_ptr_dtor ( & ssphp_z ) ;
break ;
}
zr = add_assoc_zval ( ssphp_z , " SQLSTATE " , temp ) ;
if ( zr = = FAILURE ) {
zval_ptr_dtor ( & ssphp_z ) ;
break ;
}
MAKE_STD_ZVAL ( temp ) ;
ZVAL_LONG ( temp , ssphp - > native_code ) ;
zr = add_next_index_zval ( ssphp_z , temp ) ;
if ( zr = = FAILURE ) {
zval_ptr_dtor ( & ssphp_z ) ;
break ;
}
zr = add_assoc_zval ( ssphp_z , " code " , temp ) ;
if ( zr = = FAILURE ) {
zval_ptr_dtor ( & ssphp_z ) ;
break ;
}
MAKE_STD_ZVAL ( temp ) ;
ZVAL_STRING ( temp , const_cast < char * > ( ssphp - > native_message ) , 1 ) ;
zr = add_next_index_zval ( ssphp_z , temp ) ;
if ( zr = = FAILURE ) {
zval_ptr_dtor ( & ssphp_z ) ;
break ;
}
zr = add_assoc_zval ( ssphp_z , " message " , temp ) ;
if ( zr = = FAILURE ) {
zval_ptr_dtor ( & ssphp_z ) ;
break ;
}
if ( ignore_warning ( ssphp - > sqlstate , ssphp - > native_code TSRMLS_CC ) & & ignored_chain ! = NULL ) {
zr = add_next_index_zval ( * ignored_chain , ssphp_z ) ;
if ( zr = = FAILURE ) {
zval_ptr_dtor ( & ssphp_z ) ;
break ;
}
}
else {
zr = add_next_index_zval ( * reported_chain , ssphp_z ) ;
if ( zr = = FAILURE ) {
zval_ptr_dtor ( & ssphp_z ) ;
break ;
}
}
break ; // exit the "loop" always
}
if ( ctx ) {
SQLHANDLE h = ctx - > handle ;
SQLSMALLINT h_type = ctx - > handle_type ;
for ( r = SQLGetDiagRec ( h_type , h , record_number , sql_state , & native_error , message_text , SQL_MAX_MESSAGE_LENGTH + 1 , & message_len ) ;
SQL_SUCCEEDED ( r ) ;
+ + record_number ,
r = SQLGetDiagRec ( h_type , h , record_number , sql_state , & native_error , message_text , SQL_MAX_MESSAGE_LENGTH + 1 , & message_len ) ) {
// log the result first
LOG ( log_severity , log_subsystem , " %1!s!: SQLSTATE = %2!s! " , _FN_ , sql_state ) ;
LOG ( log_severity , log_subsystem , " %1!s!: error code = %2!d! " , _FN_ , native_error ) ;
LOG ( log_severity , log_subsystem , " %1!s!: message = %2!s! " , _FN_ , message_text ) ;
MAKE_STD_ZVAL ( ssphp_z ) ;
zr = array_init ( ssphp_z ) ;
if ( zr = = FAILURE ) {
zval_ptr_dtor ( & ssphp_z ) ;
continue ;
}
// add the error info to the array
MAKE_STD_ZVAL ( temp ) ;
ZVAL_STRINGL ( temp , reinterpret_cast < char * > ( sql_state ) , SQL_SQLSTATE_SIZE , 1 ) ;
zr = add_next_index_zval ( ssphp_z , temp ) ;
if ( zr = = FAILURE ) {
zval_ptr_dtor ( & ssphp_z ) ;
continue ;
}
zr = add_assoc_zval ( ssphp_z , " SQLSTATE " , temp ) ;
if ( zr = = FAILURE ) {
zval_ptr_dtor ( & ssphp_z ) ;
continue ;
}
MAKE_STD_ZVAL ( temp ) ;
ZVAL_LONG ( temp , native_error ) ;
zr = add_next_index_zval ( ssphp_z , temp ) ;
if ( zr = = FAILURE ) {
zval_ptr_dtor ( & ssphp_z ) ;
continue ;
}
zr = add_assoc_zval ( ssphp_z , " code " , temp ) ;
if ( zr = = FAILURE ) {
zval_ptr_dtor ( & ssphp_z ) ;
continue ;
}
MAKE_STD_ZVAL ( temp ) ;
ZVAL_STRINGL ( temp , reinterpret_cast < char * > ( message_text ) , message_len , 1 ) ;
zr = add_next_index_zval ( ssphp_z , temp ) ;
if ( zr = = FAILURE ) {
zval_ptr_dtor ( & ssphp_z ) ;
continue ;
}
zr = add_assoc_zval ( ssphp_z , " message " , temp ) ;
if ( zr = = FAILURE ) {
zval_ptr_dtor ( & ssphp_z ) ;
continue ;
}
if ( ignore_warning ( reinterpret_cast < const char * > ( sql_state ) , native_error TSRMLS_CC ) & & ignored_chain ! = NULL ) {
zr = add_next_index_zval ( * ignored_chain , ssphp_z ) ;
if ( zr = = FAILURE ) {
zval_ptr_dtor ( & ssphp_z ) ;
continue ;
}
}
else {
zr = add_next_index_zval ( * reported_chain , ssphp_z ) ;
if ( zr = = FAILURE ) {
zval_ptr_dtor ( & ssphp_z ) ;
continue ;
}
}
}
}
bool all_errors_ignored = ( zend_hash_num_elements ( Z_ARRVAL_PP ( reported_chain ) ) = = reported_before ) & &
( ignored_chain ! = NULL & & zend_hash_num_elements ( Z_ARRVAL_PP ( ignored_chain ) ) > ignored_before ) ;
// if the error array came in as NULL and didn't have anything added to it, return it as NULL
if ( reported_chain_was_null & & zend_hash_num_elements ( Z_ARRVAL_PP ( reported_chain ) ) = = 0 ) {
zend_hash_destroy ( Z_ARRVAL_PP ( reported_chain ) ) ;
FREE_HASHTABLE ( Z_ARRVAL_PP ( reported_chain ) ) ;
ZVAL_NULL ( * reported_chain ) ;
}
if ( ignored_chain ! = NULL & & ignored_chain_was_null & & zend_hash_num_elements ( Z_ARRVAL_PP ( ignored_chain ) ) = = 0 ) {
zend_hash_destroy ( Z_ARRVAL_PP ( ignored_chain ) ) ;
FREE_HASHTABLE ( Z_ARRVAL_PP ( ignored_chain ) ) ;
ZVAL_NULL ( * ignored_chain ) ;
}
return all_errors_ignored ;
}
// return whether or not a warning should be ignored or returned as an error if WarningsReturnAsErrors is true
// see RINIT in init.cpp for information about which errors are ignored.
bool ignore_warning ( char const * sql_state , int native_code TSRMLS_DC )
{
for ( zend_hash_internal_pointer_reset ( SQLSRV_G ( warnings_to_ignore ) ) ;
zend_hash_has_more_elements ( SQLSRV_G ( warnings_to_ignore ) ) = = SUCCESS ;
zend_hash_move_forward ( SQLSRV_G ( warnings_to_ignore ) ) ) {
void * error_v ;
sqlsrv_error * error ;
int result ;
result = zend_hash_get_current_data ( SQLSRV_G ( warnings_to_ignore ) , ( void * * ) & error_v ) ;
if ( result = = FAILURE ) {
return false ;
}
error = static_cast < sqlsrv_error * > ( error_v ) ;
if ( ! strncmp ( error - > sqlstate , sql_state , SQL_SQLSTATE_SIZE ) & & ( error - > native_code = = native_code | | error - > native_code = = - 1 ) ) {
return true ;
}
}
return false ;
}
// used by sqlsrv_merge_zend_hash below
int sqlsrv_merge_zend_hash_dtor ( void * dest TSRMLS_DC )
{
# if defined(ZTS)
SQLSRV_UNUSED ( tsrm_ls ) ;
# endif
zval_ptr_dtor ( reinterpret_cast < zval * * > ( & dest ) ) ;
return ZEND_HASH_APPLY_REMOVE ;
}
// sqlsrv_merge_zend_hash
// merge a source hash into a dest hash table and return any errors.
bool sqlsrv_merge_zend_hash ( __inout zval * dest_z , zval const * src_z TSRMLS_DC )
{
# if defined(ZTS)
SQLSRV_UNUSED ( tsrm_ls ) ;
# endif
if ( Z_TYPE_P ( dest_z ) ! = IS_ARRAY & & Z_TYPE_P ( dest_z ) ! = IS_NULL ) DIE ( " dest_z must be an array or null " ) ;
if ( Z_TYPE_P ( src_z ) ! = IS_ARRAY & & Z_TYPE_P ( src_z ) ! = IS_NULL ) DIE ( " src_z must be an array or null " ) ;
if ( Z_TYPE_P ( src_z ) = = IS_NULL ) {
return true ;
}
HashTable * src_ht = Z_ARRVAL_P ( src_z ) ;
int result = SUCCESS ;
for ( zend_hash_internal_pointer_reset ( src_ht ) ;
zend_hash_has_more_elements ( src_ht ) = = SUCCESS ;
zend_hash_move_forward ( src_ht ) ) {
void * value_v ;
zval * value_z ;
result = zend_hash_get_current_data ( src_ht , ( void * * ) & value_v ) ;
if ( result = = FAILURE ) {
zend_hash_apply ( Z_ARRVAL_P ( dest_z ) , sqlsrv_merge_zend_hash_dtor TSRMLS_CC ) ;
return false ;
}
value_z = * ( static_cast < zval * * > ( value_v ) ) ;
result = add_next_index_zval ( dest_z , value_z ) ;
if ( result = = FAILURE ) {
zend_hash_apply ( Z_ARRVAL_P ( dest_z ) , sqlsrv_merge_zend_hash_dtor TSRMLS_CC ) ;
return false ;
}
zval_add_ref ( & value_z ) ;
}
return true ;
}
} // namespace