2016-04-12 23:43:46 +02:00
//---------------------------------------------------------------------------------------------------------------------------------
// File: pdo_util.cpp
//
// Contents: Utility functions used by both connection or statement functions
//
2016-08-24 23:28:20 +02:00
// Microsoft Drivers 4.1 for PHP for SQL Server
2016-04-12 23:43:46 +02:00
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""),
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//---------------------------------------------------------------------------------------------------------------------------------
2016-10-03 22:37:39 +02:00
# include "php_pdo_sqlsrv.h"
2016-04-12 23:43:46 +02:00
# include "zend_exceptions.h"
// *** internal constants ***
namespace {
2016-10-25 01:11:52 +02:00
const char WARNING_TEMPLATE [ ] = " SQLSTATE: %1!s! \n Error Code: %2!d! \n Error Message: %3!s! \n " ;
2016-04-12 23:43:46 +02:00
const char EXCEPTION_MSG_TEMPLATE [ ] = " SQLSTATE[%s]: %s " ;
char EXCEPTION_PROPERTY_MSG [ ] = " message " ;
char EXCEPTION_PROPERTY_CODE [ ] = " code " ;
char EXCEPTION_PROPERTY_ERRORINFO [ ] = " errorInfo " ;
const int MAX_DIGITS = 11 ; // +-2 billion = 10 digits + 1 for the sign if negative
2016-10-25 01:11:52 +02:00
// the warning message is not the error message alone; it must take WARNING_TEMPLATE above into consideration without the formats
const int WARNING_MIN_LENGTH = strlen ( WARNING_TEMPLATE ) - strlen ( " %1!s!%2!d!%3!s! " ) ;
2016-04-12 23:43:46 +02:00
// 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
SQLCHAR INTERNAL_FORMAT_ERROR [ ] = " An internal error occurred. FormatMessage failed writing an error message. " ;
// build the object and throw the PDO exception
void pdo_sqlsrv_throw_exception ( sqlsrv_error_const * error TSRMLS_DC ) ;
}
// pdo driver error messages
// errors have 3 components, the SQLSTATE (always 'IMSSP'), the error message, and an error code, which for us is always < 0
pdo_error PDO_ERRORS [ ] = {
{
SQLSRV_ERROR_DRIVER_NOT_INSTALLED ,
{ IMSSP , ( SQLCHAR * ) " This extension requires the Microsoft ODBC Driver 11 for SQL Server to "
" communicate with SQL Server. Access the following URL to download the ODBC Driver 11 for SQL Server "
" for %1!s!: "
" http://go.microsoft.com/fwlink/?LinkId=163712 " , - 1 , true }
} ,
{
SQLSRV_ERROR_ZEND_HASH ,
{ IMSSP , ( SQLCHAR * ) " An error occurred creating or accessing a Zend hash table. " , - 2 , false }
} ,
{
PDO_SQLSRV_ERROR_INVALID_OUTPUT_PARAM_TYPE ,
{ IMSSP , ( SQLCHAR * ) " An invalid PHP type was specified as an output parameter. DateTime objects, NULL values, and streams "
" cannot be specified as output parameters. " , - 3 , false }
} ,
{
SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE ,
{ IMSSP , ( SQLCHAR * ) " An invalid type for parameter %1!d! was specified. Only booleans, integers, floating point "
" numbers, strings, and streams may be used as parameters. " , - 4 , true }
} ,
{
SQLSRV_ERROR_INVALID_PARAMETER_SQLTYPE ,
{ IMSSP , ( SQLCHAR * ) " An invalid SQL Server type for parameter %1!d! was specified. " , - 5 , true }
} ,
{
SQLSRV_ERROR_INVALID_PARAMETER_ENCODING ,
{ IMSSP , ( SQLCHAR * ) " An invalid encoding was specified for parameter %1!d!. " , - 6 , true }
} ,
{
SQLSRV_ERROR_INPUT_PARAM_ENCODING_TRANSLATE ,
{ IMSSP , ( SQLCHAR * ) " An error occurred translating string for input param %1!d! to UCS-2: %2!s! " , - 7 , true }
} ,
{
SQLSRV_ERROR_OUTPUT_PARAM_ENCODING_TRANSLATE ,
{ IMSSP , ( SQLCHAR * ) " An error occurred translating string for an output param to UTF-8: %1!s! " , - 8 , true }
} ,
{
SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE ,
{ IMSSP , ( SQLCHAR * ) " An error occurred translating the connection string to UTF-16: %1!s! " , - 9 , true }
} ,
{
SQLSRV_ERROR_ZEND_STREAM ,
{ IMSSP , ( SQLCHAR * ) " An error occurred reading from a PHP stream. " , - 10 , false }
} ,
{
SQLSRV_ERROR_INPUT_STREAM_ENCODING_TRANSLATE ,
{ IMSSP , ( SQLCHAR * ) " An error occurred translating a PHP stream from UTF-8 to UTF-16: %1!s! " , - 11 , true }
} ,
{
SQLSRV_ERROR_UNKNOWN_SERVER_VERSION ,
{ IMSSP , ( SQLCHAR * ) " Failed to retrieve the server version. Unable to continue. " , - 12 , false }
} ,
{
SQLSRV_ERROR_FETCH_PAST_END ,
{ IMSSP , ( SQLCHAR * ) " There are no more rows in the active result set. Since this result set is not scrollable, "
" no more data may be retrieved. " , - 13 , false }
} ,
{
SQLSRV_ERROR_STATEMENT_NOT_EXECUTED ,
{ IMSSP , ( SQLCHAR * ) " The statement must be executed before results can be retrieved. " , - 14 , false }
} ,
{
SQLSRV_ERROR_NO_FIELDS ,
{ IMSSP , ( SQLCHAR * ) " The active result for the query contains no fields. " , - 15 , false }
} ,
{
SQLSRV_ERROR_FETCH_NOT_CALLED ,
{ IMSSP , ( SQLCHAR * ) " Internal pdo_sqlsrv error: Tried to retrieve a field before one of the PDOStatement::fetch "
" functions was called. " , - 16 , false }
} ,
{
SQLSRV_ERROR_NO_DATA ,
{ IMSSP , ( SQLCHAR * ) " Field %1!d! returned no data. " , - 17 , true }
} ,
{
SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE ,
{ IMSSP , ( SQLCHAR * ) " An error occurred translating string for a field to UTF-8: %1!s! " , - 18 , true }
} ,
{
SQLSRV_ERROR_ZEND_HASH_CREATE_FAILED ,
{ IMSSP , ( SQLCHAR * ) " Zend returned an error when creating an associative array. " , - 19 , false }
} ,
{
SQLSRV_ERROR_NEXT_RESULT_PAST_END ,
{ IMSSP , ( SQLCHAR * ) " There are no more results returned by the query. " , - 20 , false }
} ,
{
SQLSRV_ERROR_UID_PWD_BRACES_NOT_ESCAPED ,
{ IMSSP , ( SQLCHAR * ) " An unescaped right brace (}) was found in either the user name or password. All right braces must be "
" escaped with another right brace (}}). " , - 21 , false }
} ,
{
SQLSRV_ERROR_UNESCAPED_RIGHT_BRACE_IN_DSN ,
{ IMSSP , ( SQLCHAR * ) " An unescaped right brace (}) was found in the DSN string for keyword '%1!s!'. All right braces "
" must be escaped with another right brace (}}). " , - 22 , true }
} ,
{
SQLSRV_ERROR_INVALID_OPTION_TYPE_INT ,
{ IMSSP , ( SQLCHAR * ) " Invalid value type for option %1!s! was specified. Integer type was expected. " , - 23 , true }
} ,
{
SQLSRV_ERROR_INVALID_OPTION_TYPE_STRING ,
{ IMSSP , ( SQLCHAR * ) " Invalid value type for option %1!s! was specified. String type was expected. " , - 24 , true }
} ,
{
SQLSRV_ERROR_CONN_OPTS_WRONG_TYPE ,
{ IMSSP , ( SQLCHAR * ) " Expected an array of options for the connection. Connection options must be passed as an array of "
" key/value pairs. " , - 25 , false }
} ,
{
SQLSRV_ERROR_INVALID_CONNECTION_KEY ,
{ IMSSP , ( SQLCHAR * ) " An invalid connection option key type was received. Option key types must be strings. " , - 26 , false }
} ,
{
SQLSRV_ERROR_INVALID_TYPE ,
{ IMSSP , ( SQLCHAR * ) " Invalid type. " , - 27 , false }
} ,
{
PDO_SQLSRV_ERROR_INVALID_COLUMN_INDEX ,
{ IMSSP , ( SQLCHAR * ) " An invalid column number was specified. " , - 28 , false }
} ,
{
SQLSRV_ERROR_MAX_PARAMS_EXCEEDED ,
{ IMSSP , ( SQLCHAR * ) " Tried to bind parameter number %1!d!. SQL Server supports a maximum of 2100 parameters. " , - 29 , true }
} ,
{
SQLSRV_ERROR_INVALID_OPTION_KEY ,
{ IMSSP , ( SQLCHAR * ) " Invalid option key %1!s! specified. " , - 30 , true }
} ,
{
SQLSRV_ERROR_INVALID_QUERY_TIMEOUT_VALUE ,
{ IMSSP , ( SQLCHAR * ) " Invalid value %1!s! specified for option PDO::SQLSRV_ATTR_QUERY_TIMEOUT. " , - 31 , true }
} ,
{
SQLSRV_ERROR_INVALID_OPTION_SCROLLABLE ,
{ IMSSP , ( SQLCHAR * ) " The value passed for the 'Scrollable' statement option is invalid. " , - 32 , false }
} ,
{
PDO_SQLSRV_ERROR_INVALID_DBH_ATTR ,
{ IMSSP , ( SQLCHAR * ) " An invalid attribute was designated on the PDO object. " , - 33 , false }
} ,
{
PDO_SQLSRV_ERROR_INVALID_STMT_ATTR ,
{ IMSSP , ( SQLCHAR * ) " An invalid attribute was designated on the PDOStatement object. " , - 34 , false }
} ,
{
PDO_SQLSRV_ERROR_INVALID_ENCODING ,
{ IMSSP , ( SQLCHAR * ) " An invalid encoding was specified for SQLSRV_ATTR_ENCODING. " , - 35 , false }
} ,
{
PDO_SQLSRV_ERROR_INVALID_DRIVER_PARAM ,
{ IMSSP , ( SQLCHAR * ) " An invalid type or value was given for the parameter driver data. Only encoding constants "
" such as PDO::SQLSRV_ENCODING_UTF8 may be used as parameter driver options. " , - 36 , false }
} ,
{
PDO_SQLSRV_ERROR_PDO_STMT_UNSUPPORTED ,
{ IMSSP , ( SQLCHAR * ) " PDO::PARAM_STMT is not a supported parameter type. " , - 37 , false }
} ,
{
PDO_SQLSRV_ERROR_UNSUPPORTED_DBH_ATTR ,
{ IMSSP , ( SQLCHAR * ) " An unsupported attribute was designated on the PDO object. " , - 38 , false }
} ,
{
PDO_SQLSRV_ERROR_STMT_LEVEL_ATTR ,
{ IMSSP , ( SQLCHAR * ) " The given attribute is only supported on the PDOStatement object. " , - 39 , false }
} ,
{
PDO_SQLSRV_ERROR_READ_ONLY_DBH_ATTR ,
{ IMSSP , ( SQLCHAR * ) " A read-only attribute was designated on the PDO object. " , - 40 , false }
} ,
{
PDO_SQLSRV_ERROR_INVALID_DSN_STRING ,
{ IMSSP , ( SQLCHAR * ) " An invalid DSN string was specified. " , - 41 , false }
} ,
{
PDO_SQLSRV_ERROR_INVALID_DSN_KEY ,
{ IMSSP , ( SQLCHAR * ) " An invalid keyword '%1!s!' was specified in the DSN string. " , - 42 , true }
} ,
{
PDO_SQLSRV_ERROR_INVALID_STMT_OPTION ,
{ IMSSP , ( SQLCHAR * ) " An invalid statement option was specified. " , - 43 , false }
} ,
{
PDO_SQLSRV_ERROR_INVALID_CURSOR_TYPE ,
{ IMSSP , ( SQLCHAR * ) " An invalid cursor type was specified for either PDO::ATTR_CURSOR or "
" PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE " , - 44 , false }
} ,
{
PDO_SQLSRV_ERROR_PARAM_PARSE ,
{ IMSSP , ( SQLCHAR * ) " An error occurred substituting the named parameters. " , - 45 , false }
} ,
{
PDO_SQLSRV_ERROR_LAST_INSERT_ID ,
{ IMSSP , ( SQLCHAR * ) " An error occurred retrieving the last insert id. " , - 46 , false }
} ,
{
SQLSRV_ERROR_QUERY_STRING_ENCODING_TRANSLATE ,
{ IMSSP , ( SQLCHAR * ) " An error occurred translating the query string to UTF-16: %1!s!. " , - 47 , true }
} ,
{
PDO_SQLSRV_ERROR_INVALID_COLUMN_DRIVER_DATA ,
{ IMSSP , ( SQLCHAR * ) " An invalid type or value was given as bound column driver data for column %1!d!. Only "
" encoding constants such as PDO::SQLSRV_ENCODING_UTF8 may be used as bound column driver data. " , - 48 , true }
} ,
{
PDO_SQLSRV_ERROR_COLUMN_TYPE_DOES_NOT_SUPPORT_ENCODING ,
{ IMSSP , ( SQLCHAR * ) " An encoding was specified for column %1!d!. Only PDO::PARAM_LOB and PDO::PARAM_STR column types "
" can take an encoding option. " , - 49 , true }
} ,
{
PDO_SQLSRV_ERROR_INVALID_DRIVER_COLUMN_ENCODING ,
{ IMSSP , ( SQLCHAR * ) " Invalid encoding specified for column %1!d!. " , - 50 , true }
} ,
{
PDO_SQLSRV_ERROR_INVALID_DRIVER_PARAM_TYPE ,
{ IMSSP , ( SQLCHAR * ) " An encoding was specified for parameter %1!d!. Only PDO::PARAM_LOB and PDO::PARAM_STR can take an "
" encoding option. " , - 51 , true }
} ,
{
PDO_SQLSRV_ERROR_INVALID_DRIVER_PARAM_ENCODING ,
{ IMSSP , ( SQLCHAR * ) " Invalid encoding specified for parameter %1!d!. " , - 52 , true }
} ,
{
PDO_SQLSRV_ERROR_CURSOR_ATTR_AT_PREPARE_ONLY ,
{ IMSSP , ( SQLCHAR * ) " The PDO::ATTR_CURSOR and PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE attributes may only be set in the "
" $driver_options array of PDO::prepare. " , - 53 , false }
} ,
{
SQLSRV_ERROR_OUTPUT_PARAM_TRUNCATED ,
{ IMSSP , ( SQLCHAR * ) " String data, right truncated for output parameter %1!d!. " , - 54 , true }
} ,
{
SQLSRV_ERROR_INPUT_OUTPUT_PARAM_TYPE_MATCH ,
{ IMSSP , ( SQLCHAR * ) " Types for parameter value and PDO::PARAM_* constant must be compatible for input/output "
" parameter %1!d!. " , - 55 , true }
} ,
{
PDO_SQLSRV_ERROR_INVALID_PARAM_DIRECTION ,
{ IMSSP , ( SQLCHAR * ) " Invalid direction specified for parameter %1!d!. Input/output parameters must have a length. " ,
- 56 , true }
} ,
{
PDO_SQLSRV_ERROR_INVALID_OUTPUT_STRING_SIZE ,
{ IMSSP , ( SQLCHAR * ) " Invalid size for output string parameter %1!d!. Input/output string parameters must have an "
" explicit length. " , - 57 , true }
} ,
{
PDO_SQLSRV_ERROR_FUNCTION_NOT_IMPLEMENTED ,
{ IMSSP , ( SQLCHAR * ) " This function is not implemented by this driver. " , - 58 , false }
} ,
{
/* The stream related errors are not currently used in PDO, but the core layer can throw the stream related
errors so having a mapping here */
SQLSRV_ERROR_STREAMABLE_TYPES_ONLY ,
{ IMSSP , ( SQLCHAR * ) " Only char, nchar, varchar, nvarchar, binary, varbinary, and large object types can be read by using "
" streams. " , - 59 , false }
} ,
{
SQLSRV_ERROR_STREAM_CREATE ,
{ IMSSP , ( SQLCHAR * ) " An error occurred while retrieving a SQL Server field as a stream. " , - 60 , false }
} ,
{
SQLSRV_ERROR_MARS_OFF ,
{ IMSSP , ( SQLCHAR * ) " 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. " , - 61 , false }
} ,
{
SQLSRV_ERROR_FIELD_INDEX_ERROR ,
{ IMSSP , ( SQLCHAR * ) " Fields within a row must be accessed in ascending order. Cannot retrieve field %1!d! because its "
" index is less than the index of a field that has already been retrieved (%2!d!). " , - 62 , true }
} ,
{
PDO_SQLSRV_ERROR_INVALID_DSN_VALUE ,
{ IMSSP , ( SQLCHAR * ) " An invalid value was specified for the keyword '%1!s!' in the DSN string. " , - 63 , true }
} ,
{
PDO_SQLSRV_ERROR_SERVER_NOT_SPECIFIED ,
{ IMSSP , ( SQLCHAR * ) " Server keyword was not specified in the DSN string. " , - 64 , false }
} ,
{
PDO_SQLSRV_ERROR_DSN_STRING_ENDED_UNEXPECTEDLY ,
{ IMSSP , ( SQLCHAR * ) " The DSN string ended unexpectedly. " , - 65 , false }
} ,
{
PDO_SQLSRV_ERROR_EXTRA_SEMI_COLON_IN_DSN_STRING ,
{ IMSSP , ( SQLCHAR * ) " An extra semi-colon was encountered in the DSN string at character (byte-count) position '%1!d!' . " ,
- 66 , true }
} ,
{
PDO_SQLSRV_ERROR_RCB_MISSING_IN_DSN_VALUE ,
{ IMSSP , ( SQLCHAR * ) " An expected right brace (}) was not found in the DSN string for the value of the keyword '%1!s!'. " ,
- 67 , true }
} ,
{
PDO_SQLSRV_ERROR_DQ_ATTR_AT_PREPARE_ONLY ,
{ IMSSP , ( SQLCHAR * ) " The PDO::SQLSRV_ATTR_DIRECT_QUERY attribute may only be set in the $driver_options array of "
" PDO::prepare. " , - 68 , false }
} ,
{
PDO_SQLSRV_ERROR_INVALID_CURSOR_WITH_SCROLL_TYPE ,
{ IMSSP , ( SQLCHAR * ) " The PDO::SQLSRV_ATTR_CURSOR_SCROLL_TYPE attribute may only be set when PDO::ATTR_CURSOR is set to "
" PDO::CURSOR_SCROLL in the $driver_options array of PDO::prepare. " , - 69 , false }
} ,
{
SQLSRV_ERROR_INVALID_BUFFER_LIMIT ,
{ IMSSP , ( SQLCHAR * ) " The PDO::SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE attribute is not a number or the number is not "
" positive. Only positive numbers are valid for this attribute. " , - 70 , false }
} ,
{
SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED ,
{ IMSSP , ( SQLCHAR * ) " Memory limit of %1!d! KB exceeded for buffered query " , - 71 , true }
} ,
{ UINT_MAX , { } }
} ;
// Returns a sqlsrv_error for a given error code.
sqlsrv_error_const * get_error_message ( unsigned int sqlsrv_error_code ) {
sqlsrv_error_const * error_message = NULL ;
int zr = ( error_message = reinterpret_cast < sqlsrv_error_const * > ( zend_hash_index_find_ptr ( g_pdo_errors_ht , sqlsrv_error_code ) ) ) ! = NULL ? SUCCESS : FAILURE ;
if ( zr = = FAILURE ) {
DIE ( " get_error_message: zend_hash_index_find returned failure for sqlsrv_error_code = %1!d! " , sqlsrv_error_code ) ;
}
SQLSRV_ASSERT ( error_message ! = NULL , " get_error_message: error_message was null " ) ;
return error_message ;
}
// PDO error handler for the environment context.
bool pdo_sqlsrv_handle_env_error ( sqlsrv_context & ctx , unsigned int sqlsrv_error_code , bool warning TSRMLS_DC ,
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_error_auto_ptr error ;
if ( sqlsrv_error_code ! = SQLSRV_ERROR_ODBC ) {
core_sqlsrv_format_driver_error ( ctx , get_error_message ( sqlsrv_error_code ) , error , SEV_ERROR TSRMLS_CC , print_args ) ;
}
else {
bool err = core_sqlsrv_get_odbc_error ( ctx , 1 , error , SEV_ERROR TSRMLS_CC ) ;
SQLSRV_ASSERT ( err = = true , " No ODBC error was found " ) ;
}
strcpy_s ( dbh - > error_code , sizeof ( pdo_error_type ) , reinterpret_cast < const char * > ( error - > sqlstate ) ) ;
switch ( dbh - > error_mode ) {
case PDO_ERRMODE_EXCEPTION :
if ( ! warning ) {
pdo_sqlsrv_throw_exception ( error TSRMLS_CC ) ;
}
ctx . set_last_error ( error ) ;
break ;
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 ) ;
}
// pdo error handler for the dbh context.
bool pdo_sqlsrv_handle_dbh_error ( sqlsrv_context & ctx , unsigned int sqlsrv_error_code , bool warning TSRMLS_DC ,
va_list * print_args )
{
pdo_dbh_t * dbh = reinterpret_cast < pdo_dbh_t * > ( ctx . driver ( ) ) ;
SQLSRV_ASSERT ( dbh ! = NULL , " pdo_sqlsrv_handle_dbh_error: Null dbh passed " ) ;
sqlsrv_error_auto_ptr error ;
if ( sqlsrv_error_code ! = SQLSRV_ERROR_ODBC ) {
core_sqlsrv_format_driver_error ( ctx , get_error_message ( sqlsrv_error_code ) , error , SEV_ERROR TSRMLS_CC , print_args ) ;
}
else {
bool err = core_sqlsrv_get_odbc_error ( ctx , 1 , error , SEV_ERROR TSRMLS_CC ) ;
SQLSRV_ASSERT ( err = = true , " No ODBC error was found " ) ;
}
SQLSRV_ASSERT ( strlen ( 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 TSRMLS_CC ) ;
}
ctx . set_last_error ( error ) ;
break ;
case PDO_ERRMODE_WARNING :
if ( ! warning ) {
size_t msg_len = strlen ( reinterpret_cast < const char * > ( error - > native_message ) ) + SQL_SQLSTATE_BUFSIZE
2016-10-25 01:11:52 +02:00
+ 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 ,
2016-04-12 23:43:46 +02:00
error - > native_message ) ;
php_error ( E_WARNING , msg ) ;
}
ctx . set_last_error ( error ) ;
break ;
case PDO_ERRMODE_SILENT :
ctx . set_last_error ( error ) ;
break ;
default :
DIE ( " Unknown error mode. %1!d! " , dbh - > error_mode ) ;
break ;
}
// return error ignored = true for warnings.
return ( warning ? true : false ) ;
}
// PDO error handler for the statement context.
bool pdo_sqlsrv_handle_stmt_error ( sqlsrv_context & ctx , unsigned int sqlsrv_error_code , bool warning TSRMLS_DC ,
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 " ) ;
sqlsrv_error_auto_ptr error ;
if ( sqlsrv_error_code ! = SQLSRV_ERROR_ODBC ) {
core_sqlsrv_format_driver_error ( ctx , get_error_message ( sqlsrv_error_code ) , error , SEV_ERROR TSRMLS_CC , print_args ) ;
}
else {
bool err = core_sqlsrv_get_odbc_error ( ctx , 1 , error , SEV_ERROR TSRMLS_CC ) ;
SQLSRV_ASSERT ( err = = true , " No ODBC error was found " ) ;
}
SQLSRV_ASSERT ( strlen ( 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 TSRMLS_CC ) ;
}
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 ;
}
// return error ignored = true for warnings.
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
// 2, SQLSTATE of the error (driver specific error messages are 'IMSSP')
void pdo_sqlsrv_retrieve_context_error ( sqlsrv_error const * last_error , zval * pdo_zval )
{
if ( last_error ) {
// 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 ) ) ;
}
}
// Formats the error message and writes to the php error log.
void pdo_sqlsrv_log ( unsigned int severity TSRMLS_DC , const char * msg , va_list * print_args )
{
if ( ( severity & PDO_SQLSRV_G ( log_severity ) ) = = 0 ) {
return ;
}
DWORD rc = FormatMessage ( FORMAT_MESSAGE_FROM_STRING , msg , 0 , 0 , log_msg , LOG_MSG_SIZE , print_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 ) ;
}
namespace {
void pdo_sqlsrv_throw_exception ( sqlsrv_error_const * error TSRMLS_DC )
{
zval ex_obj ;
ZVAL_UNDEF ( & ex_obj ) ;
zend_class_entry * ex_class = pdo_get_exception_class ( ) ;
int zr = object_init_ex ( & ex_obj , ex_class ) ;
SQLSRV_ASSERT ( zr ! = FAILURE , " Failed to initialize exception object " ) ;
sqlsrv_malloc_auto_ptr < char > ex_msg ;
size_t ex_msg_len = strlen ( 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 ) ;
zend_update_property_string ( ex_class , & ex_obj , EXCEPTION_PROPERTY_MSG , sizeof ( EXCEPTION_PROPERTY_MSG ) - 1 ,
ex_msg TSRMLS_CC ) ;
zend_update_property_string ( ex_class , & ex_obj , EXCEPTION_PROPERTY_CODE , sizeof ( EXCEPTION_PROPERTY_CODE ) - 1 ,
reinterpret_cast < char * > ( error - > sqlstate ) TSRMLS_CC ) ;
zval ex_error_info ;
ZVAL_UNDEF ( & ex_error_info ) ;
array_init ( & ex_error_info ) ;
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 ) ) ;
2016-07-06 20:47:05 +02:00
//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
2016-04-12 23:43:46 +02:00
zend_update_property ( ex_class , & ex_obj , EXCEPTION_PROPERTY_ERRORINFO , sizeof ( EXCEPTION_PROPERTY_ERRORINFO ) - 1 ,
& ex_error_info TSRMLS_CC ) ;
2016-07-06 20:47:05 +02:00
//DELREF ex_error_info here to decrement the refcount of the zend_array is 1
//the global hashtable EG(exception) then points to the zend_object in ex_obj in zend_throw_exception_object;
//this ensure when EG(exception) cleans itself at php shutdown, the zend_array allocated is properly destroyed
Z_DELREF ( ex_error_info ) ;
2016-04-12 23:43:46 +02:00
zend_throw_exception_object ( & ex_obj TSRMLS_CC ) ;
}
}