PDO errorinfo includes additional odbc messages if available (#1133)

This commit is contained in:
Jenny Tam 2020-05-22 15:09:28 -07:00 committed by GitHub
parent d308aa4d0f
commit 06d7a496ae
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 693 additions and 150 deletions

View file

@ -99,6 +99,7 @@ install:
- echo install opencppcoverage
- choco install opencppcoverage
- set path=C:\Program Files\OpenCppCoverage;%PYTHON%;%PYTHON%\Scripts;%path%
- copy %APPVEYOR_BUILD_FOLDER%\codecov.yml c:\projects
build_script:
- copy %APPVEYOR_BUILD_FOLDER%\buildscripts\*.py c:\projects
@ -122,7 +123,7 @@ test_script:
- ps: >-
If ($env:BUILD_PLATFORM -Match "x86") {
Write-Host "Running phpt tests via OpenCppCoverage..."
OpenCppCoverage.exe --sources ${env:PHP_SRC_DIR}\*sqlsrv --modules ${env:PHP_EXE_PATH}\php*sqlsrv.dll --export_type=cobertura:c:\projects\coverage.xml --quiet --cover_children --continue_after_cpp_exception --optimized_build -- .\php.exe run-tests.php -P ${env:APPVEYOR_BUILD_FOLDER}\test\functional\ | out-file -filePath ${env:APPVEYOR_BUILD_FOLDER}\test\functional\tests.log -encoding UTF8;
OpenCppCoverage.exe --sources ${env:PHP_SRC_DIR}\*sqlsrv --modules ${env:PHP_EXE_PATH}\php*sqlsrv.dll --excluded_sources ${env:PHP_SRC_DIR}\pdo_sqlsrv\shared\core_stream.cpp --export_type=cobertura:c:\projects\coverage.xml --quiet --cover_children --continue_after_cpp_exception --optimized_build -- .\php.exe run-tests.php -P ${env:APPVEYOR_BUILD_FOLDER}\test\functional\ | out-file -filePath ${env:APPVEYOR_BUILD_FOLDER}\test\functional\tests.log -encoding UTF8;
Write-Host "Showing the last 25 lines of the log file..."
Get-Content ${env:APPVEYOR_BUILD_FOLDER}\test\functional\tests.log -Tail 25;
ls *.xml

View file

@ -43,8 +43,11 @@ const int WARNING_MIN_LENGTH = static_cast<const int>( strlen( WARNING_TEMPLATE
sqlsrv_error_const* get_error_message( _In_opt_ unsigned int sqlsrv_error_code);
// build the object and throw the PDO exception
void pdo_sqlsrv_throw_exception( _In_ sqlsrv_error_const* error );
void pdo_sqlsrv_throw_exception(_In_ sqlsrv_error const* error);
void format_or_get_all_errors(_Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _Inout_ sqlsrv_error_auto_ptr& error, _Inout_ char* error_code, _In_opt_ va_list* print_args);
void add_remaining_errors_to_array (_In_ sqlsrv_error const* error, _Inout_ zval* array_z);
}
// pdo driver error messages
@ -462,47 +465,26 @@ pdo_error PDO_ERRORS[] = {
{ UINT_MAX, {} }
};
// PDO error handler for the environment context.
bool pdo_sqlsrv_handle_env_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ bool warning,
_In_opt_ va_list* print_args )
{
SQLSRV_ASSERT(( ctx != NULL ), "pdo_sqlsrv_handle_env_error: sqlsrv_context was null" );
pdo_dbh_t* dbh = reinterpret_cast<pdo_dbh_t*>( ctx.driver());
SQLSRV_ASSERT(( dbh != NULL ), "pdo_sqlsrv_handle_env_error: pdo_dbh_t was null" );
SQLSRV_ASSERT((ctx != NULL), "pdo_sqlsrv_handle_env_error: sqlsrv_context was null");
pdo_dbh_t* dbh = reinterpret_cast<pdo_dbh_t*>(ctx.driver());
SQLSRV_ASSERT((dbh != NULL), "pdo_sqlsrv_handle_env_error: pdo_dbh_t was null");
sqlsrv_error_auto_ptr error;
format_or_get_all_errors(ctx, sqlsrv_error_code, error, dbh->error_code, print_args);
if( sqlsrv_error_code != SQLSRV_ERROR_ODBC ) {
core_sqlsrv_format_driver_error( ctx, get_error_message( sqlsrv_error_code ), error, SEV_ERROR, print_args );
}
else {
bool err = core_sqlsrv_get_odbc_error( ctx, 1, error, SEV_ERROR );
SQLSRV_ASSERT( err == true, "No ODBC error was found" );
// error_mode is valid because PDO API has already taken care of invalid ones
if (!warning && dbh->error_mode == PDO_ERRMODE_EXCEPTION) {
pdo_sqlsrv_throw_exception(error);
}
strcpy_s( dbh->error_code, sizeof( pdo_error_type ), reinterpret_cast<const char*>( error->sqlstate ));
ctx.set_last_error(error);
switch( dbh->error_mode ) {
case PDO_ERRMODE_EXCEPTION:
if( !warning ) {
pdo_sqlsrv_throw_exception( error );
}
ctx.set_last_error( error );
break;
default:
DIE( "pdo_sqlsrv_handle_env_error: Unexpected error mode. %1!d!", dbh->error_mode );
break;
}
// we don't transfer the zval_auto_ptr since set_last_error increments the zval ref count
// return error ignored = true for warnings.
return ( warning ? true : false );
return (warning ? true : false);
}
// pdo error handler for the dbh context.
@ -513,95 +495,50 @@ bool pdo_sqlsrv_handle_dbh_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned
SQLSRV_ASSERT( dbh != NULL, "pdo_sqlsrv_handle_dbh_error: Null dbh passed" );
sqlsrv_error_auto_ptr error;
format_or_get_all_errors(ctx, sqlsrv_error_code, error, dbh->error_code, print_args);
if( sqlsrv_error_code != SQLSRV_ERROR_ODBC ) {
core_sqlsrv_format_driver_error( ctx, get_error_message( sqlsrv_error_code ), error, SEV_ERROR, print_args );
}
else {
bool err = core_sqlsrv_get_odbc_error( ctx, 1, error, SEV_ERROR );
SQLSRV_ASSERT( err == true, "No ODBC error was found" );
// error_mode is valid because PDO API has already taken care of invalid ones
if (!warning) {
if (dbh->error_mode == PDO_ERRMODE_EXCEPTION) {
pdo_sqlsrv_throw_exception(error);
}
else if (dbh->error_mode == PDO_ERRMODE_WARNING) {
size_t msg_len = strnlen_s(reinterpret_cast<const char*>(error->native_message)) + SQL_SQLSTATE_BUFSIZE
+ MAX_DIGITS + WARNING_MIN_LENGTH + 1;
sqlsrv_malloc_auto_ptr<char> msg;
msg = static_cast<char*>(sqlsrv_malloc(msg_len));
core_sqlsrv_format_message(msg, static_cast<unsigned int>(msg_len), WARNING_TEMPLATE, error->sqlstate, error->native_code,
error->native_message);
php_error(E_WARNING, "%s", msg.get());
}
}
SQLSRV_ASSERT(strnlen_s(reinterpret_cast<const char*>(error->sqlstate)) <= sizeof(dbh->error_code), "Error code overflow");
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 );
}
ctx.set_last_error( error );
break;
case PDO_ERRMODE_WARNING:
if( !warning ) {
size_t msg_len = strnlen_s( reinterpret_cast<const char*>( error->native_message )) + SQL_SQLSTATE_BUFSIZE
+ MAX_DIGITS + WARNING_MIN_LENGTH + 1;
sqlsrv_malloc_auto_ptr<char> msg;
msg = static_cast<char*>( sqlsrv_malloc( msg_len ) );
core_sqlsrv_format_message( msg, static_cast<unsigned int>( msg_len ), WARNING_TEMPLATE, error->sqlstate, error->native_code,
error->native_message );
php_error(E_WARNING, "%s", msg.get());
}
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;
}
ctx.set_last_error(error);
// return error ignored = true for warnings.
return ( warning ? true : false );
return (warning ? true : false);
}
// PDO error handler for the statement context.
bool pdo_sqlsrv_handle_stmt_error( _Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ bool warning,
_In_opt_ va_list* print_args )
bool pdo_sqlsrv_handle_stmt_error(_Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _In_opt_ bool warning,
_In_opt_ va_list* print_args)
{
pdo_stmt_t* pdo_stmt = reinterpret_cast<pdo_stmt_t*>( ctx.driver());
SQLSRV_ASSERT( pdo_stmt != NULL && pdo_stmt->dbh != NULL, "pdo_sqlsrv_handle_stmt_error: Null statement or dbh passed" );
pdo_stmt_t* pdo_stmt = reinterpret_cast<pdo_stmt_t*>(ctx.driver());
SQLSRV_ASSERT(pdo_stmt != NULL && pdo_stmt->dbh != NULL, "pdo_sqlsrv_handle_stmt_error: Null statement or dbh passed");
sqlsrv_error_auto_ptr error;
format_or_get_all_errors(ctx, sqlsrv_error_code, error, pdo_stmt->error_code, print_args);
if( sqlsrv_error_code != SQLSRV_ERROR_ODBC ) {
core_sqlsrv_format_driver_error( ctx, get_error_message( sqlsrv_error_code ), error, SEV_ERROR, print_args );
}
else {
bool err = core_sqlsrv_get_odbc_error( ctx, 1, error, SEV_ERROR );
SQLSRV_ASSERT( err == true, "No ODBC error was found" );
}
SQLSRV_ASSERT( strnlen_s( reinterpret_cast<const char*>( error->sqlstate ) ) <= sizeof( pdo_stmt->error_code ), "Error code overflow");
strcpy_s( pdo_stmt->error_code, sizeof( pdo_stmt->error_code ), reinterpret_cast<const char*>( error->sqlstate ));
switch( pdo_stmt->dbh->error_mode ) {
case PDO_ERRMODE_EXCEPTION:
if( !warning ) {
pdo_sqlsrv_throw_exception( error );
}
ctx.set_last_error( error );
break;
case PDO_ERRMODE_WARNING:
ctx.set_last_error( error );
break;
case PDO_ERRMODE_SILENT:
ctx.set_last_error( error );
break;
default:
DIE( "Unknown error mode. %1!d!", pdo_stmt->dbh->error_mode );
break;
// error_mode is valid because PDO API has already taken care of invalid ones
if (!warning && pdo_stmt->dbh->error_mode == PDO_ERRMODE_EXCEPTION) {
pdo_sqlsrv_throw_exception(error);
}
ctx.set_last_error(error);
// return error ignored = true for warnings.
return ( warning ? true : false );
return (warning ? true : false);
}
// Transfer a sqlsrv_context's error to a PDO zval. The standard format for a zval error is 3 elements:
// 0, native code
// 1, native message
@ -613,6 +550,8 @@ void pdo_sqlsrv_retrieve_context_error( _In_ sqlsrv_error const* last_error, _Ou
// SQLSTATE is already present in the zval.
add_next_index_long( pdo_zval, last_error->native_code );
add_next_index_string( pdo_zval, reinterpret_cast<char*>( last_error->native_message ));
add_remaining_errors_to_array (last_error, pdo_zval);
}
}
@ -639,7 +578,7 @@ sqlsrv_error_const* get_error_message( _In_opt_ unsigned int sqlsrv_error_code)
return error_message;
}
void pdo_sqlsrv_throw_exception( _In_ sqlsrv_error_const* error )
void pdo_sqlsrv_throw_exception(_In_ sqlsrv_error const* error)
{
zval ex_obj;
ZVAL_UNDEF( &ex_obj );
@ -650,10 +589,10 @@ void pdo_sqlsrv_throw_exception( _In_ sqlsrv_error_const* error )
SQLSRV_ASSERT( zr != FAILURE, "Failed to initialize exception object" );
sqlsrv_malloc_auto_ptr<char> ex_msg;
size_t ex_msg_len = strnlen_s( reinterpret_cast<const char*>( error->native_message )) + SQL_SQLSTATE_BUFSIZE +
size_t ex_msg_len = strnlen_s(reinterpret_cast<const char*>(error->native_message)) + SQL_SQLSTATE_BUFSIZE +
12 + 1; // 12 = "SQLSTATE[]: "
ex_msg = reinterpret_cast<char*>( sqlsrv_malloc( ex_msg_len ));
snprintf( ex_msg, ex_msg_len, EXCEPTION_MSG_TEMPLATE, error->sqlstate, error->native_message );
ex_msg = reinterpret_cast<char*>(sqlsrv_malloc(ex_msg_len));
snprintf(ex_msg, ex_msg_len, EXCEPTION_MSG_TEMPLATE, error->sqlstate, error->native_message);
zend_update_property_string( ex_class, &ex_obj, EXCEPTION_PROPERTY_MSG, sizeof( EXCEPTION_PROPERTY_MSG ) - 1,
ex_msg );
zend_update_property_string( ex_class, &ex_obj, EXCEPTION_PROPERTY_CODE, sizeof( EXCEPTION_PROPERTY_CODE ) - 1,
@ -665,6 +604,9 @@ void pdo_sqlsrv_throw_exception( _In_ sqlsrv_error_const* error )
add_next_index_string( &ex_error_info, reinterpret_cast<char*>( error->sqlstate ));
add_next_index_long( &ex_error_info, error->native_code );
add_next_index_string( &ex_error_info, reinterpret_cast<char*>( error->native_message ));
add_remaining_errors_to_array (error, &ex_error_info);
//zend_update_property makes an entry in the properties_table in ex_obj point to the Z_ARRVAL( ex_error_info )
//and the refcount of the zend_array is incremented by 1
zend_update_property( ex_class, &ex_obj, EXCEPTION_PROPERTY_ERRORINFO, sizeof( EXCEPTION_PROPERTY_ERRORINFO ) - 1,
@ -677,4 +619,59 @@ void pdo_sqlsrv_throw_exception( _In_ sqlsrv_error_const* error )
zend_throw_exception_object( &ex_obj );
}
void add_remaining_errors_to_array (_In_ sqlsrv_error const* error, _Inout_ zval* array_z)
{
if (error->next != NULL && PDO_SQLSRV_G(report_additional_errors)) {
sqlsrv_error *p = error->next;
while (p != NULL) {
// check if sql state or native message is NULL and handle them accordingly
char *state = "";
char *msg = "";
if (p->sqlstate != NULL) {
state = reinterpret_cast<char*>(p->sqlstate);
}
if (p->native_message != NULL) {
msg = reinterpret_cast<char*>(p->native_message);
}
add_next_index_string(array_z, state);
add_next_index_long(array_z, p->native_code);
add_next_index_string(array_z, msg);
p = p-> next;
}
}
}
void format_or_get_all_errors(_Inout_ sqlsrv_context& ctx, _In_opt_ unsigned int sqlsrv_error_code, _Inout_ sqlsrv_error_auto_ptr& error, _Inout_ char* error_code, _In_opt_ va_list* print_args)
{
if (sqlsrv_error_code != SQLSRV_ERROR_ODBC) {
core_sqlsrv_format_driver_error(ctx, get_error_message(sqlsrv_error_code), error, SEV_ERROR, print_args);
strcpy_s(error_code, sizeof(pdo_error_type), reinterpret_cast<const char*>(error->sqlstate));
}
else {
bool result = core_sqlsrv_get_odbc_error(ctx, 1, error, SEV_ERROR, true);
if (result) {
// Check if there exist more errors
int rec_number = 2;
sqlsrv_error_auto_ptr err;
sqlsrv_error *p = error;
do {
result = core_sqlsrv_get_odbc_error(ctx, rec_number++, err, SEV_ERROR, true);
if (result) {
p->next = err.get();
err.transferred();
p = p->next;
}
} while (result);
}
// core_sqlsrv_get_odbc_error() returns the error_code of size SQL_SQLSTATE_BUFSIZE,
// which is the same size as pdo_error_type
strcpy_s(error_code, sizeof(pdo_error_type), reinterpret_cast<const char*>(error->sqlstate));
}
}
}

View file

@ -31,6 +31,7 @@ ZEND_BEGIN_MODULE_GLOBALS(pdo_sqlsrv)
unsigned int pdo_log_severity;
zend_long client_buffer_max_size;
short report_additional_errors;
#ifndef _WIN32
zend_long set_locale_info;

View file

@ -53,6 +53,7 @@ extern HMODULE g_sqlsrv_hmodule;
// (these are defined as macros to allow concatenation as we do below)
#define INI_PDO_SQLSRV_CLIENT_BUFFER_MAX_SIZE "client_buffer_max_kb_size"
#define INI_PDO_SQLSRV_LOG "log_severity"
#define INI_PDO_SQLSRV_MORE_ERRORS "report_additional_errors"
#define INI_PREFIX "pdo_sqlsrv."
#ifndef _WIN32
@ -64,6 +65,7 @@ PHP_INI_BEGIN()
zend_pdo_sqlsrv_globals, pdo_sqlsrv_globals )
STD_PHP_INI_ENTRY( INI_PREFIX INI_PDO_SQLSRV_CLIENT_BUFFER_MAX_SIZE , INI_BUFFERED_QUERY_LIMIT_DEFAULT, PHP_INI_ALL, OnUpdateLong,
client_buffer_max_size, zend_pdo_sqlsrv_globals, pdo_sqlsrv_globals )
STD_PHP_INI_ENTRY(INI_PREFIX INI_PDO_SQLSRV_MORE_ERRORS, "1", PHP_INI_ALL, OnUpdateLong, report_additional_errors, zend_pdo_sqlsrv_globals, pdo_sqlsrv_globals)
#ifndef _WIN32
STD_PHP_INI_ENTRY(INI_PREFIX INI_PDO_SET_LOCALE_INFO, "2", PHP_INI_ALL, OnUpdateLong, set_locale_info,
zend_pdo_sqlsrv_globals, pdo_sqlsrv_globals)

View file

@ -776,6 +776,7 @@ struct sqlsrv_error_const {
// subclass which is used by the core layer to instantiate ODBC errors
struct sqlsrv_error : public sqlsrv_error_const {
struct sqlsrv_error *next; // Only used in pdo_sqlsrv for additional errors (as a linked list)
sqlsrv_error( void )
{
@ -783,16 +784,18 @@ struct sqlsrv_error : public sqlsrv_error_const {
native_message = NULL;
native_code = -1;
format = false;
next = NULL;
}
sqlsrv_error( _In_ SQLCHAR* sql_state, _In_ SQLCHAR* message, _In_ SQLINTEGER code, _In_ bool printf_format = false )
sqlsrv_error( _In_ SQLCHAR* sql_state, _In_ SQLCHAR* message, _In_ SQLINTEGER code, _In_ bool printf_format = false)
{
sqlstate = reinterpret_cast<SQLCHAR*>( sqlsrv_malloc( SQL_SQLSTATE_BUFSIZE ));
native_message = reinterpret_cast<SQLCHAR*>( sqlsrv_malloc( SQL_MAX_ERROR_MESSAGE_LENGTH + 1 ));
strcpy_s( reinterpret_cast<char*>( sqlstate ), SQL_SQLSTATE_BUFSIZE, reinterpret_cast<const char*>( sql_state ));
strcpy_s( reinterpret_cast<char*>( native_message ), SQL_MAX_ERROR_MESSAGE_LENGTH + 1, reinterpret_cast<const char*>( message ));
sqlstate = reinterpret_cast<SQLCHAR*>(sqlsrv_malloc(SQL_SQLSTATE_BUFSIZE));
native_message = reinterpret_cast<SQLCHAR*>(sqlsrv_malloc(SQL_MAX_ERROR_MESSAGE_LENGTH + 1));
strcpy_s(reinterpret_cast<char*>(sqlstate), SQL_SQLSTATE_BUFSIZE, reinterpret_cast<const char*>(sql_state));
strcpy_s(reinterpret_cast<char*>(native_message), SQL_MAX_ERROR_MESSAGE_LENGTH + 1, reinterpret_cast<const char*>(message));
native_code = code;
format = printf_format;
next = NULL;
}
sqlsrv_error( _In_ sqlsrv_error_const const& prototype )
@ -802,16 +805,26 @@ struct sqlsrv_error : public sqlsrv_error_const {
~sqlsrv_error( void )
{
if( sqlstate != NULL ) {
sqlsrv_free( sqlstate );
reset();
}
void reset() {
if (sqlstate != NULL) {
sqlsrv_free(sqlstate);
sqlstate = NULL;
}
if( native_message != NULL ) {
sqlsrv_free( native_message );
if (native_message != NULL) {
sqlsrv_free(native_message);
native_message = NULL;
}
if (next != NULL) {
next->reset(); // free the next sqlsrv_error, and so on
sqlsrv_free(next);
next = NULL;
}
}
};
// an auto_ptr for sqlsrv_errors. These call the destructor explicitly rather than call delete
class sqlsrv_error_auto_ptr : public sqlsrv_auto_ptr<sqlsrv_error, sqlsrv_error_auto_ptr > {
@ -852,7 +865,6 @@ public:
}
};
//*********************************************************************************************************************************
// Context
//*********************************************************************************************************************************
@ -1901,7 +1913,7 @@ enum error_handling_flags {
// 3/message) driver specific error message
// The fetch type determines if the indices are numeric, associative, or both.
bool core_sqlsrv_get_odbc_error( _Inout_ sqlsrv_context& ctx, _In_ int record_number, _Inout_ sqlsrv_error_auto_ptr& error,
_In_ logging_severity severity );
_In_ logging_severity severity, _In_ bool check_warning = false );
// format and return a driver specfic error
void core_sqlsrv_format_driver_error( _In_ sqlsrv_context& ctx, _In_ sqlsrv_error_const const* custom_error,

View file

@ -257,8 +257,7 @@ void convert_datetime_string_to_zval(_Inout_ sqlsrv_stmt* stmt, _In_opt_ char* i
// 3/message) driver specific error message
// The fetch type determines if the indices are numeric, associative, or both.
bool core_sqlsrv_get_odbc_error( _Inout_ sqlsrv_context& ctx, _In_ int record_number, _Inout_ sqlsrv_error_auto_ptr& error, _In_ logging_severity severity
)
bool core_sqlsrv_get_odbc_error( _Inout_ sqlsrv_context& ctx, _In_ int record_number, _Inout_ sqlsrv_error_auto_ptr& error, _In_ logging_severity severity, _In_ bool check_warning /* = false */)
{
SQLHANDLE h = ctx.handle();
SQLSMALLINT h_type = ctx.handle_type();
@ -297,17 +296,9 @@ bool core_sqlsrv_get_odbc_error( _Inout_ sqlsrv_context& ctx, _In_ int record_nu
r = SQLGetDiagRecW( h_type, h, record_number, wsqlstate, &error->native_code, wnative_message,
SQL_MAX_ERROR_MESSAGE_LENGTH + 1, &wmessage_len );
// don't use the CHECK* macros here since it will trigger reentry into the error handling system
// Workaround for a bug in unixODBC 2.3.4 when connection pooling is enabled (PDO SQLSRV).
// Instead of returning false, we return an empty error message to prevent the driver from throwing an exception.
// To reproduce:
// Create a connection and close it (return it to the pool)
// Create a new connection from the pool.
// Prepare and execute a statement that generates an info message (such as 'USE tempdb;')
#ifdef __APPLE__
if( r == SQL_NO_DATA && ctx.driver() != NULL /*PDO SQLSRV*/ ) {
r = SQL_SUCCESS;
}
#endif // __APPLE__
// removed the workaround for Mac users with unixODBC 2.3.4 when connection pooling is enabled (PDO SQLSRV), for two reasons:
// (1) not recommended to use connection pooling with unixODBC < 2.3.7
// (2) the problem was not reproducible with unixODBC 2.3.7
if( !SQL_SUCCEEDED( r ) || r == SQL_NO_DATA ) {
return false;
}
@ -348,6 +339,15 @@ bool core_sqlsrv_get_odbc_error( _Inout_ sqlsrv_context& ctx, _In_ int record_nu
break;
}
// Only overrides 'severity' if 'check_warning' is true (false by default)
if (check_warning) {
// The character string value returned for an SQLSTATE consists of a two-character class value
// followed by a three-character subclass value. A class value of "01" indicates a warning.
// https://docs.microsoft.com/sql/odbc/reference/appendixes/appendix-a-odbc-error-codes?view=sql-server-ver15
if (error->sqlstate[0] == '0' && error->sqlstate[1] == '1') {
severity = SEV_WARNING;
}
}
// log the error first
LOG( severity, "%1!s!: SQLSTATE = %2!s!", ctx.func(), error->sqlstate );

View file

@ -20,5 +20,8 @@ Array
\(
\[0\] => 42S02
\[1\] => 208
\[2\] => \[Microsoft\]\[ODBC Driver 1[1-9] for SQL Server\]\[SQL Server\]Invalid object name 'Person.Addressx'.
\[2\] => \[Microsoft\]\[ODBC Driver 1[1-9] for SQL Server\]\[SQL Server\]Invalid object name 'Person.Addressx'\.
\[3\] => 42000
\[4\] => 8180
\[5\] => \[Microsoft\]\[ODBC Driver 1[1-9] for SQL Server\]\[SQL Server\]Statement\(s\) could not be prepared\.
\)

View file

@ -23,4 +23,7 @@ Array
\[0\] => 42S22
\[1\] => 207
\[2\] => \[Microsoft\]\[ODBC Driver 1[1-9] for SQL Server\]\[SQL Server\]Invalid column name 'Cityx'.
\[3\] => 42000
\[4\] => 8180
\[5\] => \[Microsoft\]\[ODBC Driver 1[1-9] for SQL Server\]\[SQL Server\]Statement\(s\) could not be prepared\.
\)

View file

@ -3,7 +3,11 @@ PDO_SQLSRV Connection Pooling Test on Unix
--DESCRIPTION--
This test assumes the default odbcinst.ini has not been modified.
--SKIPIF--
<?php if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') die("Skipped: Test for Linux and Mac"); ?>
<?php
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
die("Skip test for Linux and Mac only.");
}
?>
--FILE--
<?php
function findODBCDriver($content, $lines_to_add)

View file

@ -1,12 +1,12 @@
<?php
include 'MsSetup.inc';
$conn1 = new PDO("sqlsrv:Server=$server; database=$databaseName; driver=$driver", $uid, $pwd);
$connId1 = ConnectionID($conn1);
$conn1 = null;
$connId1 = connectionID($conn1);
unset($conn1);
$conn2 = new PDO("sqlsrv:Server=$server; database=$databaseName; driver=$driver", $uid, $pwd);
$connId2 = ConnectionID($conn2);
$conn2 = null;
$connId2 = connectionID($conn2);
if ($connId1 === $connId2){
echo "Pooled\n";
@ -14,7 +14,21 @@ if ($connId1 === $connId2){
echo "Not Pooled\n";
}
function ConnectionID($conn)
// The following is not applicable in Azure
$azure = isAzure($conn2);
if (!$azure) {
try {
$conn2->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $conn2->prepare("SET NOCOUNT ON; USE tempdb; SELECT 1/0 AS col1");
$stmt->execute();
} catch (PDOException $e) {
checkErrorInfo($stmt, $e);
}
}
unset($conn2);
function connectionID($conn)
{
$tsql = "SELECT [connection_id] FROM [sys].[dm_exec_connections] where session_id = @@SPID";
$stmt = $conn->query($tsql);
@ -23,4 +37,42 @@ function ConnectionID($conn)
$stmt = null;
return ($connID);
}
function isAzure($conn)
{
try {
$tsql = "SELECT SERVERPROPERTY ('edition')";
$stmt = $conn->query($tsql);
$result = $stmt->fetch(PDO::FETCH_NUM);
$edition = $result[0];
if ($edition === "SQL Azure") {
return true;
} else {
return false;
}
} catch (PDOException $e) {
echo $e->getMessage();
die("Could not fetch server property.");
}
}
function checkErrorInfo($stmt, $err)
{
$expected = "*Divide by zero error encountered*";
$idx = count($err->errorInfo) - 1;
$failed = false;
if ($idx != 5 || !fnmatch($expected, $err->errorInfo[$idx])) {
echo "Error message unexpected!\n";
$failed = true;
}
if ($err->errorInfo !== $stmt->errorInfo()) {
echo "Error info arrays should match!\n";
$failed = true;
}
if ($failed) {
var_dump($err);
}
}
?>

View file

@ -0,0 +1,156 @@
--TEST--
GitHub issue 924 - Wrong error message after switching database context
--DESCRIPTION--
Verifies that the user has the option to see the following error message after the first one.
--SKIPIF--
<?php require('skipif_azure.inc'); ?>
--FILE--
<?php
require_once("MsSetup.inc");
$tsql = "SET NOCOUNT ON; USE $databaseName; SELECT 1/0 AS col1";
$errorInfo = array("01000", 5701, "*Changed database context to '$databaseName'.");
$errorInfo2 = array("22012", 8134, "*Divide by zero error encountered*");
function compareErrorInfo($actualErrorInfo, $errorInfo, $index = 0)
{
if (($actualErrorInfo[$index] != $errorInfo[0]) ||
($actualErrorInfo[$index + 1] != $errorInfo[1]) ||
!fnmatch($errorInfo[2], $actualErrorInfo[$index + 2])) {
echo "Expected this: \n";
var_dump($errorInfo);
echo "Actual error info is: \n";
var_dump($actualErrorInfo);
}
}
function compare2ErrorInfo($actualErrorInfo)
{
global $errorInfo, $errorInfo2;
if (count($actualErrorInfo) != 6) {
echo "Expect 6 elements in the error info!\n";
var_dump($actualErrorInfo);
return;
}
// Compare the first three elements
compareErrorInfo($actualErrorInfo, $errorInfo);
compareErrorInfo($actualErrorInfo, $errorInfo2, 3);
}
function checkException($conn, $on)
{
global $tsql, $errorInfo, $errorInfo2;
ini_set('pdo_sqlsrv.report_additional_errors', $on);
try {
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$stmt = $conn->prepare($tsql);
$stmt->execute();
var_dump($stmt->fetchColumn());
echo "Exception should have been thrown!\n";
} catch (PDOException $e) {
// compare errorInfo arrays from both the exception object and the stmt object
if ($on) {
compare2ErrorInfo($e->errorInfo);
compare2ErrorInfo($stmt->errorInfo());
}
else {
compareErrorInfo($e->errorInfo, $errorInfo);
compareErrorInfo($stmt->errorInfo(), $errorInfo);
}
}
unset($stmt);
unset($conn);
}
function checkWarning($conn, $on)
{
global $tsql, $errorInfo, $errorInfo2;
ini_set('pdo_sqlsrv.report_additional_errors', $on);
try {
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
$stmt = $conn->prepare($tsql);
$stmt->execute();
compareErrorInfo($stmt->errorInfo(), $errorInfo);
if ($on) {
compareErrorInfo($stmt->errorInfo(), $errorInfo2, 3);
} else {
echo count($stmt->errorInfo()) . PHP_EOL;
}
} catch (PDOException $e) {
echo " Warnings are logged but do not expect exceptions.\n";
var_dump($e);
}
unset($stmt);
unset($conn);
}
try {
// This forces PHP to log errors rather than displaying errors on screen
ini_set('display_errors', '0');
ini_set('log_errors', '1');
$logFilename = 'php_924_errors.log';
$logFilepath = dirname(__FILE__).'/'.$logFilename;
if (file_exists($logFilepath)) {
unlink($logFilepath);
}
ini_set('error_log', $logFilepath);
ini_set('pdo_sqlsrv.log_severity', '2'); // warnings only
$conn = new PDO("sqlsrv:server=$server;", $uid, $pwd);
checkWarning($conn, 1);
checkException($conn, 1);
checkWarning($conn, 0);
checkException($conn, 0);
if (file_exists($logFilepath)) {
echo file_get_contents($logFilepath);
unlink($logFilepath);
} else {
echo "Expected to find the log file\n";
}
} catch (PDOException $e) {
var_dump($e);
}
echo "\nDone\n";
?>
--EXPECTF--
3
[%s UTC] pdo_sqlsrv_db_handle_factory: SQLSTATE = 01000
[%s UTC] pdo_sqlsrv_db_handle_factory: error code = 5701
[%s UTC] pdo_sqlsrv_db_handle_factory: message = %s[SQL Server]Changed database context to 'master'.
[%s UTC] pdo_sqlsrv_db_handle_factory: SQLSTATE = 01000
[%s UTC] pdo_sqlsrv_db_handle_factory: error code = 5703
[%s UTC] pdo_sqlsrv_db_handle_factory: message = %s[SQL Server]Changed language setting to us_english.
[%s UTC] pdo_sqlsrv_stmt_execute: SQLSTATE = 01000
[%s UTC] pdo_sqlsrv_stmt_execute: error code = 5701
[%s UTC] pdo_sqlsrv_stmt_execute: message = %s[SQL Server]Changed database context to '%s'.
[%s UTC] PHP Warning: PDOStatement::execute(): SQLSTATE[01000]: Warning: 5701 %s[SQL Server]Changed database context to '%s'. in %spdo_924_display_more_errors.php on line %d
[%s UTC] pdo_sqlsrv_stmt_execute: SQLSTATE = 01000
[%s UTC] pdo_sqlsrv_stmt_execute: error code = 5701
[%s UTC] pdo_sqlsrv_stmt_execute: message = %s[SQL Server]Changed database context to '%s'.
[%s UTC] pdo_sqlsrv_stmt_execute: SQLSTATE = 01000
[%s UTC] pdo_sqlsrv_stmt_execute: error code = 5701
[%s UTC] pdo_sqlsrv_stmt_execute: message = %s[SQL Server]Changed database context to '%s'.
[%s UTC] PHP Warning: PDOStatement::execute(): SQLSTATE[01000]: Warning: 5701 %s[SQL Server]Changed database context to '%s'. in %spdo_924_display_more_errors.php on line %d
[%s UTC] pdo_sqlsrv_stmt_execute: SQLSTATE = 01000
[%s UTC] pdo_sqlsrv_stmt_execute: error code = 5701
[%s UTC] pdo_sqlsrv_stmt_execute: message = %s[SQL Server]Changed database context to '%s'.
Done

View file

@ -0,0 +1,239 @@
--TEST--
GitHub issue 924 - verifies the warnings or error messages are logged to a log file
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
function toConnect()
{
require("MsSetup.inc");
$dsn = getDSN($server, $databaseName, $driver);
$conn = new PDO($dsn, $uid, $pwd);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $conn;
}
function printCursor($cursorArray)
{
if ($cursorArray[PDO::ATTR_CURSOR] == PDO::CURSOR_FWDONLY) {
$cursor = 'FORWARD ONLY cursor';
} else {
switch ($cursorArray[PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE]) {
case PDO::SQLSRV_CURSOR_DYNAMIC:
$cursor = 'server side DYNAMIC cursor';
break;
case PDO::SQLSRV_CURSOR_STATIC:
$cursor = 'server side STATIC cursor';
break;
case PDO::SQLSRV_CURSOR_KEYSET:
$cursor = 'server side KEYSET cursor';
break;
case PDO::SQLSRV_CURSOR_BUFFERED:
$cursor = 'client side BUFFERED cursor';
break;
default:
$cursor = 'error';
break;
}
}
echo "#####Testing $cursor#####\n";
return $cursor;
}
function checkResults($data, $results, $resultSet, $expectedRows)
{
$failed = false;
for ($j = 0; $j < $expectedRows; $j++) {
if ($results[$j][0] != $data[$resultSet][$j]) {
$failed = true;
echo "Fetched results unexpected at row $j:\n";
print_r($results[$j]);
break;
}
}
return $failed;
}
try {
ini_set('log_errors', '1');
$logFilename = 'php_924_cursors.log';
$logFilepath = dirname(__FILE__).'/'.$logFilename;
if (file_exists($logFilepath)) {
unlink($logFilepath);
}
ini_set('error_log', $logFilepath);
ini_set('pdo_sqlsrv.log_severity', '3'); // warnings and errors only
// All supported cursor types
$cursors = array(array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY),
array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL, PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE => PDO::SQLSRV_CURSOR_DYNAMIC),
array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL, PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE => PDO::SQLSRV_CURSOR_STATIC),
array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL, PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE => PDO::SQLSRV_CURSOR_KEYSET),
array(PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL, PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE => PDO::SQLSRV_CURSOR_BUFFERED),
);
// Data for testing, all integer types
$data = array(array(86, -217483648, 0, -432987563, 7, 217483647),
array(0, 31, 127, 255, 1, 10),
array(4534, -212, 32767, 0, 7, -32768),
array(-1, 546098342985600, 9223372000000000000, 5115115115115, 7, -7),
array(0, 1, 0, 0, 1, 1),
);
$tableName = 'pdo_924_batchquery_test';
// Column names
$colName = array('c1_int', 'c2_tinyint', 'c3_smallint', 'c4_bigint', 'c5_bit');
$columns = array(new ColumnMeta('int', $colName[0]),
new ColumnMeta('tinyint', $colName[1]),
new ColumnMeta('smallint',$colName[2]),
new ColumnMeta('bigint', $colName[3]),
new ColumnMeta('bit', $colName[4]));
$conn = toConnect();
createTable($conn, $tableName, $columns);
$expectedRows = sizeof($data[0]);
// Expected result sets = number of columns, since the batch fetches each column sequentially
$expectedResultSets = count($colName);
// Insert each row. Need an associative array to use insertRow()
for ($i = 0; $i < $expectedRows; ++$i) {
$inputs = array();
for ($j = 0; $j < $expectedResultSets; ++$j) {
$inputs[$colName[$j]] = $data[$j][$i];
}
$stmt = insertRow($conn, $tableName, $inputs);
unset($stmt);
}
$query = "SELECT c1_int FROM $tableName;
SELECT c2_tinyint FROM $tableName;
SELECT c3_smallint FROM $tableName;
SELECT c4_bigint FROM $tableName;
SELECT c5_bit FROM $tableName;";
for ($i = 0; $i < sizeof($cursors); ++$i) {
$cursorType = $cursors[$i];
// $cursor = printCursor($i);
$cursor = printCursor($cursorType);
$stmt = $conn->prepare($query, $cursorType);
$stmt->execute();
$numResultSets = 0;
do {
$res = $stmt->fetchAll(PDO::FETCH_NUM);
$failed = checkResults($data, $res, $numResultSets, $expectedRows);
++$numResultSets;
} while (!$failed && $stmt->nextRowset());
if ($numResultSets != $expectedResultSets) {
echo ("Unexpected number of result sets, expected $expectedResultedSets, got $numResultSets\n");
break;
}
if (file_exists($logFilepath)) {
echo file_get_contents($logFilepath);
unlink($logFilepath);
}
unset($stmt);
echo "#####Finished testing with $cursor#####\n";
}
// Now reset logging by disabling it
ini_set('pdo_sqlsrv.log_severity', '0');
dropTable($conn, $tableName);
unset($conn);
} catch (PDOException $e) {
var_dump($e->errorInfo);
}
echo "Done.\n";
?>
--EXPECTF--
#####Testing FORWARD ONLY cursor#####
[%s UTC] pdo_sqlsrv_db_handle_factory: SQLSTATE = 01000
[%s UTC] pdo_sqlsrv_db_handle_factory: error code = 5701
[%s UTC] pdo_sqlsrv_db_handle_factory: message = %s[SQL Server]Changed database context to '%s'.
[%s UTC] pdo_sqlsrv_db_handle_factory: SQLSTATE = 01000
[%s UTC] pdo_sqlsrv_db_handle_factory: error code = 5703
[%s UTC] pdo_sqlsrv_db_handle_factory: message = %s[SQL Server]Changed language setting to us_english.
#####Finished testing with FORWARD ONLY cursor#####
#####Testing server side DYNAMIC cursor#####
[%s UTC] pdo_sqlsrv_stmt_execute: SQLSTATE = 01S02
[%s UTC] pdo_sqlsrv_stmt_execute: error code = 0
[%s UTC] pdo_sqlsrv_stmt_execute: message = %sCursor type changed
[%s UTC] pdo_sqlsrv_stmt_execute: SQLSTATE = 01000
[%s UTC] pdo_sqlsrv_stmt_execute: error code = 16954
[%s UTC] pdo_sqlsrv_stmt_execute: message = %s[SQL Server]Executing SQL directly; no cursor.
[%s UTC] pdo_sqlsrv_stmt_next_rowset: SQLSTATE = 01S02
[%s UTC] pdo_sqlsrv_stmt_next_rowset: error code = 0
[%s UTC] pdo_sqlsrv_stmt_next_rowset: message = %sCursor type changed
[%s UTC] pdo_sqlsrv_stmt_next_rowset: SQLSTATE = 01S02
[%s UTC] pdo_sqlsrv_stmt_next_rowset: error code = 0
[%s UTC] pdo_sqlsrv_stmt_next_rowset: message = %sCursor type changed
[%s UTC] pdo_sqlsrv_stmt_next_rowset: SQLSTATE = 01S02
[%s UTC] pdo_sqlsrv_stmt_next_rowset: error code = 0
[%s UTC] pdo_sqlsrv_stmt_next_rowset: message = %sCursor type changed
[%s UTC] pdo_sqlsrv_stmt_next_rowset: SQLSTATE = 01S02
[%s UTC] pdo_sqlsrv_stmt_next_rowset: error code = 0
[%s UTC] pdo_sqlsrv_stmt_next_rowset: message = %sCursor type changed
#####Finished testing with server side DYNAMIC cursor#####
#####Testing server side STATIC cursor#####
[%s UTC] pdo_sqlsrv_stmt_execute: SQLSTATE = 01S02
[%s UTC] pdo_sqlsrv_stmt_execute: error code = 0
[%s UTC] pdo_sqlsrv_stmt_execute: message = %sCursor type changed
[%s UTC] pdo_sqlsrv_stmt_execute: SQLSTATE = 01000
[%s UTC] pdo_sqlsrv_stmt_execute: error code = 16954
[%s UTC] pdo_sqlsrv_stmt_execute: message = %s[SQL Server]Executing SQL directly; no cursor.
[%s UTC] pdo_sqlsrv_stmt_next_rowset: SQLSTATE = 01S02
[%s UTC] pdo_sqlsrv_stmt_next_rowset: error code = 0
[%s UTC] pdo_sqlsrv_stmt_next_rowset: message = %sCursor type changed
[%s UTC] pdo_sqlsrv_stmt_next_rowset: SQLSTATE = 01S02
[%s UTC] pdo_sqlsrv_stmt_next_rowset: error code = 0
[%s UTC] pdo_sqlsrv_stmt_next_rowset: message = %sCursor type changed
[%s UTC] pdo_sqlsrv_stmt_next_rowset: SQLSTATE = 01S02
[%s UTC] pdo_sqlsrv_stmt_next_rowset: error code = 0
[%s UTC] pdo_sqlsrv_stmt_next_rowset: message = %sCursor type changed
[%s UTC] pdo_sqlsrv_stmt_next_rowset: SQLSTATE = 01S02
[%s UTC] pdo_sqlsrv_stmt_next_rowset: error code = 0
[%s UTC] pdo_sqlsrv_stmt_next_rowset: message = %sCursor type changed
#####Finished testing with server side STATIC cursor#####
#####Testing server side KEYSET cursor#####
[%s UTC] pdo_sqlsrv_stmt_execute: SQLSTATE = 01S02
[%s UTC] pdo_sqlsrv_stmt_execute: error code = 0
[%s UTC] pdo_sqlsrv_stmt_execute: message = %sCursor type changed
[%s UTC] pdo_sqlsrv_stmt_execute: SQLSTATE = 01000
[%s UTC] pdo_sqlsrv_stmt_execute: error code = 16954
[%s UTC] pdo_sqlsrv_stmt_execute: message = %s[SQL Server]Executing SQL directly; no cursor.
[%s UTC] pdo_sqlsrv_stmt_next_rowset: SQLSTATE = 01S02
[%s UTC] pdo_sqlsrv_stmt_next_rowset: error code = 0
[%s UTC] pdo_sqlsrv_stmt_next_rowset: message = %sCursor type changed
[%s UTC] pdo_sqlsrv_stmt_next_rowset: SQLSTATE = 01S02
[%s UTC] pdo_sqlsrv_stmt_next_rowset: error code = 0
[%s UTC] pdo_sqlsrv_stmt_next_rowset: message = %sCursor type changed
[%s UTC] pdo_sqlsrv_stmt_next_rowset: SQLSTATE = 01S02
[%s UTC] pdo_sqlsrv_stmt_next_rowset: error code = 0
[%s UTC] pdo_sqlsrv_stmt_next_rowset: message = %sCursor type changed
[%s UTC] pdo_sqlsrv_stmt_next_rowset: SQLSTATE = 01S02
[%s UTC] pdo_sqlsrv_stmt_next_rowset: error code = 0
[%s UTC] pdo_sqlsrv_stmt_next_rowset: message = %sCursor type changed
#####Finished testing with server side KEYSET cursor#####
#####Testing client side BUFFERED cursor#####
#####Finished testing with client side BUFFERED cursor#####
Done.

View file

@ -44,6 +44,9 @@ try {
} else {
echo "$logFilepath is missing!\n";
}
// Now reset logging by disabling it
ini_set('pdo_sqlsrv.log_severity', '0');
echo "Done\n";
} catch (Exception $e) {
@ -56,6 +59,9 @@ try {
[%s UTC] pdo_sqlsrv_db_handle_factory: SQLSTATE = 01000
[%s UTC] pdo_sqlsrv_db_handle_factory: error code = 5701
[%s UTC] pdo_sqlsrv_db_handle_factory: message = %s[SQL Server]Changed database context to '%s'.
[%s UTC] pdo_sqlsrv_db_handle_factory: SQLSTATE = 01000
[%s UTC] pdo_sqlsrv_db_handle_factory: error code = 5703
[%s UTC] pdo_sqlsrv_db_handle_factory: message = %s[SQL Server]Changed language setting to %s.
[%s UTC] pdo_sqlsrv_dbh_prepare: entering
[%s UTC] pdo_sqlsrv_stmt_execute: entering
[%s UTC] pdo_sqlsrv_stmt_describe_col: entering

View file

@ -5,21 +5,36 @@ Test the PDO::errorCode() and PDO::errorInfo() methods.
--FILE--
<?php
require_once("MsCommon_mid-refactor.inc");
require_once("MsData_PDO_AllTypes.inc");
try {
$db = connect();
$tbname = "PDO_MainTypes";
createTableMainTypes($db, $tbname);
// query with a wrong column name.
$db->query("SELECT * FROM $tbname WHERE IntColX = 1");
$tbname = "PDO_test_error";
// create a dummy table
createTable($db, $tbname, array(new ColumnMeta("int", "id")));
try {
// query with a wrong column name -- catch the exception and show errors
$stmt = $db->query("SELECT * FROM $tbname WHERE IntColX = 1");
echo "Should have thrown an exception!\n";
} catch (PDOException $e) {
echo $db->errorCode() . PHP_EOL;
if ($e->getCode() != $db->errorCode()) {
echo "Error codes do not match!\n";
echo $e->getCode() . PHP_EOL;
}
$info = $db->errorInfo();
print_r($info);
if ($e->errorInfo != $info) {
echo "Error info arrays do not match!\n";
print_r($e->errorInfo);
}
}
dropTable($db, $tbname);
unset($conn);
unset($db);
} catch (PDOException $e) {
print($db->errorCode());
echo "\n";
print_r($db->errorInfo());
var_dump($e);
}
?>
--EXPECTREGEX--
@ -29,4 +44,7 @@ Array
\[0\] => 42S22
\[1\] => 207
\[2\] => \[Microsoft\]\[ODBC Driver 1[1-9] for SQL Server\]\[SQL Server\]Invalid column name 'IntColX'\.
\[3\] => 42000
\[4\] => 8180
\[5\] => \[Microsoft\]\[ODBC Driver 1[1-9] for SQL Server\]\[SQL Server\]Statement\(s\) could not be prepared\.
\)

View file

@ -96,6 +96,9 @@ Done with 0
[%s UTC] pdo_sqlsrv_stmt_execute: SQLSTATE = 42S02
[%s UTC] pdo_sqlsrv_stmt_execute: error code = 208
[%s UTC] pdo_sqlsrv_stmt_execute: message = %s[SQL Server]Invalid object name 'temp_table'.
[%s UTC] pdo_sqlsrv_stmt_execute: SQLSTATE = 42000
[%s UTC] pdo_sqlsrv_stmt_execute: error code = 8180
[%s UTC] pdo_sqlsrv_stmt_execute: message = %s[SQL Server]Statement(s) could not be prepared.
Done with 1
[%s UTC] PHP Warning: PDO::query(): SQLSTATE[42S02]: Base table or view not found: 208 %s[SQL Server]Invalid object name 'temp_table'. in %spdo_errorMode_logs.php on line %d
@ -112,5 +115,8 @@ Done with 4
[%s UTC] pdo_sqlsrv_stmt_execute: SQLSTATE = 42S02
[%s UTC] pdo_sqlsrv_stmt_execute: error code = 208
[%s UTC] pdo_sqlsrv_stmt_execute: message = %s[SQL Server]Invalid object name 'temp_table'.
[%s UTC] pdo_sqlsrv_stmt_execute: SQLSTATE = 42000
[%s UTC] pdo_sqlsrv_stmt_execute: error code = 8180
[%s UTC] pdo_sqlsrv_stmt_execute: message = %s[SQL Server]Statement(s) could not be prepared.
Done with -1

View file

@ -0,0 +1,43 @@
--TEST--
Error checking for multiple active row sets (MARS) disabled
--DESCRIPTION--
This is similar to sqlsrv srv_053_mars_disabled_error_checks.phpt to check the errors
when multiple active row sets (MARS) is disabled.
--SKIPIF--
<?php require('skipif_mid-refactor.inc'); ?>
--FILE--
<?php
require_once("MsSetup.inc");
try {
$conn = new PDO("sqlsrv:server=$server; MultipleActiveResultSets = false", $uid, $pwd);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql1 = "SELECT 'ONE'";
$sql2 = "SELECT 'TWO'";
$stmt1 = $conn->query($sql1);
$stmt2 = $conn->query($sql2);
$res = [$stmt1->fetch(), $stmt2->fetch()];
var_dump($res);
unset($stmt1);
unset($stmt2);
unset($conn);
} catch (PDOException $e) {
var_dump($e->errorInfo);
}
echo "\nDone\n";
?>
--EXPECT--
array(3) {
[0]=>
string(5) "IMSSP"
[1]=>
int(-61)
[2]=>
string(313) "The connection cannot process this operation because there is a statement with pending results. To make the connection available for other queries, either fetch all results or cancel or free the statement. For more information, see the product documentation about the MultipleActiveResultSets connection option."
}
Done