RC release code

This commit is contained in:
Meet Bhagdev 2016-06-13 14:44:53 -07:00
parent 3dd0ebaa44
commit de040a9327
17 changed files with 2384 additions and 2306 deletions

View file

@ -42,7 +42,7 @@ const int INFO_BUFFER_LEN = 256;
const char* PROCESSOR_ARCH[] = { "x86", "x64", "ia64" };
// ODBC driver name.
const char CONNECTION_STRING_DRIVER_NAME[] = "Driver={ODBC Driver 11 for SQL Server};";
const char* CONNECTION_STRING_DRIVER_NAME[] = {"Driver={ODBC Driver 13 for SQL Server};", "Driver={ODBC Driver 11 for SQL Server};"};
// default options if only the server is specified
const char CONNECTION_STRING_DEFAULT_OPTIONS[] = "Mars_Connection={Yes}";
@ -124,7 +124,8 @@ sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_
conn = conn_factory( temp_conn_h, err, driver TSRMLS_CC );
conn->set_func( driver_func );
for ( std::size_t i = DRIVER_VERSION::MIN; i <= DRIVER_VERSION::MAX; ++i ) {
conn_str = CONNECTION_STRING_DRIVER_NAME[i];
build_connection_string_and_set_conn_attr( conn, server, uid, pwd, options_ht, valid_conn_opts, driver,
conn_str TSRMLS_CC );
@ -151,11 +152,20 @@ sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_
SQLCHAR state[SQL_SQLSTATE_BUFSIZE];
SQLSMALLINT len;
SQLRETURN r = SQLGetDiagField( SQL_HANDLE_DBC, conn->handle(), 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len );
bool missing_driver_error = ( SQL_SUCCEEDED( r ) && state[0] == 'I' && state[1] == 'M' && state[2] == '0' && state[3] == '0' &&
state[4] == '2' );
// if it's a IM002, meaning that the correct ODBC driver is not installed
CHECK_CUSTOM_ERROR( SQL_SUCCEEDED( r ) && state[0] == 'I' && state[1] == 'M' && state[2] == '0' && state[3] == '0' &&
state[4] == '2', conn, SQLSRV_ERROR_DRIVER_NOT_INSTALLED, get_processor_arch() ) {
CHECK_CUSTOM_ERROR( missing_driver_error && ( i == DRIVER_VERSION::MAX ), conn, SQLSRV_ERROR_DRIVER_NOT_INSTALLED, get_processor_arch()) {
throw core::CoreException();
}
if ( !missing_driver_error ) {
break;
}
} else {
conn->driver_version = static_cast<DRIVER_VERSION>( i );
break;
}
}
CHECK_SQL_ERROR( r, conn ) {
throw core::CoreException();
@ -533,8 +543,6 @@ void build_connection_string_and_set_conn_attr( sqlsrv_conn* conn, const char* s
try {
connection_string = CONNECTION_STRING_DRIVER_NAME;
// Add the server name
common_conn_str_append_func( ODBCConnOptions::SERVER, server, strlen( server ), connection_string TSRMLS_CC );
@ -586,22 +594,17 @@ void build_connection_string_and_set_conn_attr( sqlsrv_conn* conn, const char* s
}
}
for( zend_hash_internal_pointer_reset( options );
zend_hash_has_more_elements( options ) == SUCCESS;
zend_hash_move_forward( options )) {
int type = HASH_KEY_NON_EXISTENT;
zend_string *key = NULL;
zend_ulong index = 0;
zend_ulong index = -1;
zval* data = NULL;
type = zend_hash_get_current_key( options, &key, &index );
ZEND_HASH_FOREACH_KEY_VAL( options, index, key, data ) {
int type = HASH_KEY_NON_EXISTENT;
type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG;
// The driver layer should ensure a valid key.
DEBUG_SQLSRV_ASSERT(( type == HASH_KEY_IS_LONG ), "build_connection_string_and_set_conn_attr: invalid connection option key type." );
core::sqlsrv_zend_hash_get_current_data( *conn, options, data TSRMLS_CC );
conn_opt = get_connection_option( conn, index, valid_conn_opts TSRMLS_CC );
if( index == SQLSRV_CONN_OPTION_MARS ) {
@ -609,7 +612,7 @@ void build_connection_string_and_set_conn_attr( sqlsrv_conn* conn, const char* s
}
conn_opt->func( conn_opt, data, conn, connection_string TSRMLS_CC );
}
} ZEND_HASH_FOREACH_END();
// MARS on if not explicitly turned off
if( !mars_mentioned ) {
@ -755,7 +758,7 @@ size_t core_str_zval_is_true( zval* value_z )
}
// save adjustments to the value made by stripping whitespace at the end
core::sqlsrv_zval_stringl( value_z, value_in, val_len);
Z_STRLEN_P( value_z ) = val_len;
const char VALID_TRUE_VALUE_1[] = "true";
const char VALID_TRUE_VALUE_2[] = "1";

View file

@ -970,6 +970,14 @@ enum SERVER_VERSION {
SERVER_VERSION_2008, // use this for anything 2008 or later
};
// supported driver versions.
enum DRIVER_VERSION : size_t {
MIN = 0,
ODBC_DRIVER_13 = MIN,
ODBC_DRIVER_11 = 1,
MAX = ODBC_DRIVER_11,
};
// forward decl
struct sqlsrv_stmt;
struct stmt_option;
@ -981,6 +989,8 @@ struct sqlsrv_conn : public sqlsrv_context {
// instance variables
SERVER_VERSION server_version; // version of the server that we're connected to
DRIVER_VERSION driver_version;
// initialize with default values
sqlsrv_conn( SQLHANDLE h, error_callback e, void* drv, SQLSRV_ENCODING encoding TSRMLS_DC ) :
sqlsrv_context( h, SQL_HANDLE_DBC, e, drv, encoding )
@ -1258,7 +1268,7 @@ struct sqlsrv_stmt : public sqlsrv_context {
unsigned int current_stream_read; // # of bytes read so far. (if we read an empty PHP stream, we send an empty string
// to the server)
zval field_cache; // cache for a single row of fields, to allow multiple and out of order retrievals
zval* active_stream; // the currently active stream reading data from the database
zval active_stream; // the currently active stream reading data from the database
sqlsrv_stmt( sqlsrv_conn* c, SQLHANDLE handle, error_callback e, void* drv TSRMLS_DC );
virtual ~sqlsrv_stmt( void );
@ -1313,7 +1323,7 @@ void core_sqlsrv_execute( sqlsrv_stmt* stmt TSRMLS_DC, const char* sql = NULL, i
field_meta_data* core_sqlsrv_field_metadata( sqlsrv_stmt* stmt, SQLSMALLINT colno TSRMLS_DC );
bool core_sqlsrv_fetch( sqlsrv_stmt* stmt, SQLSMALLINT fetch_orientation, SQLULEN fetch_offset TSRMLS_DC );
void core_sqlsrv_get_field(sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_phptype sqlsrv_phptype, bool prefer_string,
__out void** field_value, __out SQLLEN* field_length, bool cache_field,
__out void*& field_value, __out SQLLEN* field_length, bool cache_field,
__out SQLSRV_PHPTYPE *sqlsrv_php_type_out TSRMLS_DC);
bool core_sqlsrv_has_any_result( sqlsrv_stmt* stmt TSRMLS_DC );
void core_sqlsrv_next_result( sqlsrv_stmt* stmt TSRMLS_DC, bool finalize_output_params = true, bool throw_on_errors = true );
@ -1325,6 +1335,7 @@ void core_sqlsrv_set_send_at_exec( sqlsrv_stmt* stmt, zval* value_z TSRMLS_DC );
bool core_sqlsrv_send_stream_packet( sqlsrv_stmt* stmt TSRMLS_DC );
void core_sqlsrv_set_buffered_query_limit( sqlsrv_stmt* stmt, zval* value_z TSRMLS_DC );
void core_sqlsrv_set_buffered_query_limit( sqlsrv_stmt* stmt, SQLLEN limit TSRMLS_DC );
void core_finalize_output_parameters(sqlsrv_stmt* stmt TSRMLS_DC);
//*********************************************************************************************************************************
@ -1564,8 +1575,8 @@ enum SQLSRV_ERROR_CODES {
};
// the message returned by ODBC Driver 11 for SQL Server
const char CONNECTION_BUSY_ODBC_ERROR[] = "[Microsoft][ODBC Driver 11 for SQL Server]Connection is busy with results for "
"another command";
static const char* CONNECTION_BUSY_ODBC_ERROR[] = { "[Microsoft][ODBC Driver 13 for SQL Server]Connection is busy with results for another command",
"[Microsoft][ODBC Driver 11 for SQL Server]Connection is busy with results for another command" };
// SQLSTATE for all internal errors
extern SQLCHAR IMSSP[];
@ -1742,9 +1753,9 @@ namespace core {
throw CoreException();
}
if(( len == sizeof( CONNECTION_BUSY_ODBC_ERROR ) - 1 ) &&
!strcmp( reinterpret_cast<const char*>( err_msg ), CONNECTION_BUSY_ODBC_ERROR )) {
std::size_t driver_version = stmt->conn->driver_version;
if(( len == sizeof( CONNECTION_BUSY_ODBC_ERROR[driver_version] ) - 1 ) &&
!strcmp( reinterpret_cast<const char*>( err_msg ), CONNECTION_BUSY_ODBC_ERROR[driver_version] )) {
THROW_CORE_ERROR( stmt, SQLSRV_ERROR_MARS_OFF );
}

View file

@ -81,7 +81,7 @@ size_t calc_utf8_missing( sqlsrv_stmt* stmt, const char* buffer, size_t buffer_e
bool check_for_next_stream_parameter( sqlsrv_stmt* stmt TSRMLS_DC );
bool convert_input_param_to_utf16( zval* input_param_z, zval* convert_param_z );
void core_get_field_common(__inout sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_phptype
sqlsrv_php_type, __out void** field_value, __out SQLLEN* field_len TSRMLS_DC );
sqlsrv_php_type, __out void*& field_value, __out SQLLEN* field_len TSRMLS_DC);
// returns the ODBC C type constant that matches the PHP type and encoding given
SQLSMALLINT default_c_type( sqlsrv_stmt* stmt, SQLULEN paramno, zval const* param_z, SQLSRV_ENCODING encoding TSRMLS_DC );
void default_sql_size_and_scale( sqlsrv_stmt* stmt, unsigned int paramno, zval* param_z, SQLSRV_ENCODING encoding,
@ -92,7 +92,7 @@ void default_sql_type( sqlsrv_stmt* stmt, SQLULEN paramno, zval* param_z, SQLSRV
void field_cache_dtor( zval* data_z );
void finalize_output_parameters( sqlsrv_stmt* stmt TSRMLS_DC );
void get_field_as_string( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_phptype sqlsrv_php_type,
__out void** field_value, __out SQLLEN* field_len TSRMLS_DC );
__out void*& field_value, __out SQLLEN* field_len TSRMLS_DC );
stmt_option const* get_stmt_option( sqlsrv_conn const* conn, zend_ulong key, const stmt_option stmt_opts[] TSRMLS_DC );
bool is_valid_sqlsrv_phptype( sqlsrv_phptype type );
// assure there is enough space for the output parameter string
@ -127,9 +127,9 @@ sqlsrv_stmt::sqlsrv_stmt( sqlsrv_conn* c, SQLHANDLE handle, error_callback e, vo
current_stream( NULL, SQLSRV_ENCODING_DEFAULT ),
current_stream_read( 0 ),
query_timeout( QUERY_TIMEOUT_INVALID ),
buffered_query_limit( sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_INVALID ),
active_stream( NULL )
buffered_query_limit( sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_INVALID )
{
ZVAL_UNDEF( &active_stream );
// initialize the input string parameters array (which holds zvals)
core::sqlsrv_array_init( *conn, &param_input_strings TSRMLS_CC );
@ -152,7 +152,7 @@ sqlsrv_stmt::sqlsrv_stmt( sqlsrv_conn* c, SQLHANDLE handle, error_callback e, vo
// desctructor for sqlsrv statement.
sqlsrv_stmt::~sqlsrv_stmt( void )
{
if( active_stream ) {
if( Z_TYPE( active_stream ) != IS_UNDEF ) {
TSRMLS_FETCH();
close_active_stream( this TSRMLS_CC );
}
@ -248,21 +248,17 @@ sqlsrv_stmt* core_sqlsrv_create_stmt( sqlsrv_conn* conn, driver_stmt_factory stm
// process the options array given to core_sqlsrv_prepare.
if( options_ht && zend_hash_num_elements( options_ht ) > 0 ) {
for( zend_hash_internal_pointer_reset( options_ht );
zend_hash_has_more_elements( options_ht ) == SUCCESS;
zend_hash_move_forward( options_ht )) {
zend_string *key = NULL;
zend_ulong index = -1;
zend_string *key = NULL;
zval* value_z = NULL;
int type = zend_hash_get_current_key( options_ht, &key, &index);
ZEND_HASH_FOREACH_KEY_VAL( options_ht, index, key, value_z ) {
int type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG;
// The driver layer should ensure a valid key.
DEBUG_SQLSRV_ASSERT(( type == HASH_KEY_IS_LONG ), "allocate_stmt: Invalid statment option key provided." );
core::sqlsrv_zend_hash_get_current_data( *(stmt->conn), options_ht, value_z TSRMLS_CC );
const stmt_option* stmt_opt = get_stmt_option( stmt->conn, index, valid_stmt_opts TSRMLS_CC );
// if the key didn't match, then return the error to the script.
@ -271,9 +267,7 @@ sqlsrv_stmt* core_sqlsrv_create_stmt( sqlsrv_conn* conn, driver_stmt_factory stm
// perform the actions the statement option needs done.
(*stmt_opt->func)( stmt, stmt_opt, value_z TSRMLS_CC );
}
zend_hash_internal_pointer_end( options_ht );
} ZEND_HASH_FOREACH_END();
}
sqlsrv_stmt* return_stmt = stmt;
@ -658,13 +652,13 @@ void core_sqlsrv_bind_param(sqlsrv_stmt* stmt, SQLUSMALLINT param_num, SQLSMALLI
void core_sqlsrv_execute( sqlsrv_stmt* stmt TSRMLS_DC, const char* sql, int sql_len )
{
SQLRETURN r;
try {
// close the stream to release the resource
close_active_stream( stmt TSRMLS_CC );
SQLRETURN r;
if( sql ) {
sqlsrv_malloc_auto_ptr<wchar_t> wsql_string;
@ -705,8 +699,9 @@ void core_sqlsrv_execute( sqlsrv_stmt* stmt TSRMLS_DC, const char* sql, int sql_
finalize_output_parameters( stmt TSRMLS_CC );
}
// stream parameters are sent, clean the Hashtable
if ( stmt->send_streams_at_exec ) {
zend_hash_clean( Z_ARRVAL( stmt->param_streams ));
}
}
catch( core::CoreException& e ) {
@ -869,7 +864,7 @@ field_meta_data* core_sqlsrv_field_metadata( sqlsrv_stmt* stmt, SQLSMALLINT coln
// Nothing, excpetion thrown if an error occurs
void core_sqlsrv_get_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_phptype sqlsrv_php_type_in, bool prefer_string,
__out void** field_value, __out SQLLEN* field_len, bool cache_field,
__out void*& field_value, __out SQLLEN* field_len, bool cache_field,
__out SQLSRV_PHPTYPE *sqlsrv_php_type_out TSRMLS_DC)
{
try {
@ -879,20 +874,20 @@ void core_sqlsrv_get_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_
// if the field has been retrieved before, return the previous result
field_cache* cached = NULL;
if (NULL != (cached = reinterpret_cast<field_cache*>(zend_hash_index_find_ptr(Z_ARRVAL(stmt->field_cache), static_cast<zend_ulong>(field_index))))) {
if (NULL != ( cached = static_cast<field_cache*>( zend_hash_index_find_ptr( Z_ARRVAL( stmt->field_cache ), static_cast<zend_ulong>( field_index ))))) {
// the field value is NULL
if( cached->value == NULL ) {
*field_value = NULL;
field_value = NULL;
*field_len = 0;
if( sqlsrv_php_type_out ) { *sqlsrv_php_type_out = SQLSRV_PHPTYPE_NULL; }
}
else {
*field_value = sqlsrv_malloc( cached->len, sizeof( char ), 1 );
memcpy( *field_value, cached->value, cached->len );
field_value = sqlsrv_malloc( cached->len, sizeof( char ), 1 );
memcpy( field_value, cached->value, cached->len );
if( cached->type.typeinfo.type == SQLSRV_PHPTYPE_STRING) {
// prevent the 'string not null terminated' warning
reinterpret_cast<char*>( *field_value )[ cached->len ] = '\0';
reinterpret_cast<char*>( field_value )[ cached->len ] = '\0';
}
*field_len = cached->len;
if( sqlsrv_php_type_out) { *sqlsrv_php_type_out = static_cast<SQLSRV_PHPTYPE>(cached->type.typeinfo.type); }
@ -921,9 +916,9 @@ void core_sqlsrv_get_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_
core_sqlsrv_get_field( stmt, i, invalid, prefer_string, field_value, field_len, cache_field,
sqlsrv_php_type_out TSRMLS_CC );
// delete the value returned since we only want it cached, not the actual value
if( *field_value ) {
efree( *field_value );
*field_value = NULL;
if( field_value ) {
efree( field_value );
field_value = NULL;
*field_len = 0;
}
}
@ -955,7 +950,7 @@ void core_sqlsrv_get_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_
// if the user wants us to cache the field, we'll do it
if( cache_field ) {
field_cache cache( *field_value, *field_len, sqlsrv_php_type );
field_cache cache( field_value, *field_len, sqlsrv_php_type );
core::sqlsrv_zend_hash_index_update_mem( *stmt, Z_ARRVAL( stmt->field_cache ), field_index, &cache, sizeof(field_cache) TSRMLS_CC );
}
}
@ -1304,6 +1299,9 @@ bool core_sqlsrv_send_stream_packet( sqlsrv_stmt* stmt TSRMLS_DC )
return true;
}
void core_finalize_output_parameters(sqlsrv_stmt* stmt TSRMLS_DC) {
finalize_output_parameters(stmt TSRMLS_CC);
}
void stmt_option_functor::operator()( sqlsrv_stmt* /*stmt*/, stmt_option const* /*opt*/, zval* /*value_z*/ TSRMLS_DC )
{
@ -1334,20 +1332,20 @@ void stmt_option_buffered_query_limit:: operator()( sqlsrv_stmt* stmt, stmt_opti
void close_active_stream( __inout sqlsrv_stmt* stmt TSRMLS_DC )
{
// if there is no active stream, return
if( stmt->active_stream == NULL ) {
if( Z_TYPE( stmt->active_stream ) == IS_UNDEF ) {
return;
}
php_stream* stream = NULL;
// we use no verify since verify would return immediately and we want to assert, not return.
php_stream_from_zval_no_verify( stream, stmt->active_stream );
php_stream_from_zval_no_verify( stream, &( stmt->active_stream ));
SQLSRV_ASSERT(( stream != NULL ), "close_active_stream: Unknown resource type as our active stream." );
php_stream_close( stream ); // this will NULL out the active stream in the statement. We don't check for errors here.
SQLSRV_ASSERT( stmt->active_stream == NULL, "close_active_stream: Active stream not closed." );
SQLSRV_ASSERT( Z_TYPE( stmt->active_stream ) == IS_UNDEF, "close_active_stream: Active stream not closed." );
}
@ -1471,7 +1469,7 @@ size_t calc_utf8_missing( sqlsrv_stmt* stmt, const char* buffer, size_t buffer_e
// the driver layer would have to calculate size of the field_value
// to decide the amount of memory allocation.
void core_get_field_common( __inout sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_phptype
sqlsrv_php_type, __out void** field_value, __out SQLLEN* field_len TSRMLS_DC )
sqlsrv_php_type, __out void*& field_value, __out SQLLEN* field_len TSRMLS_DC )
{
try {
@ -1507,11 +1505,11 @@ void core_get_field_common( __inout sqlsrv_stmt* stmt, SQLUSMALLINT field_index,
}
if( *field_len == SQL_NULL_DATA ) {
*field_value = NULL;
field_value = NULL;
break;
}
*field_value = field_value_temp;
field_value = field_value_temp;
field_value_temp.transferred();
break;
}
@ -1533,11 +1531,11 @@ void core_get_field_common( __inout sqlsrv_stmt* stmt, SQLUSMALLINT field_index,
}
if( *field_len == SQL_NULL_DATA ) {
*field_value = NULL;
field_value = NULL;
break;
}
*field_value = field_value_temp;
field_value = field_value_temp;
field_value_temp.transferred();
break;
}
@ -1573,7 +1571,7 @@ void core_get_field_common( __inout sqlsrv_stmt* stmt, SQLUSMALLINT field_index,
if( *field_len == SQL_NULL_DATA ) {
ZVAL_NULL( return_value_z );
*field_value = reinterpret_cast<void*>( return_value_z.get() );
field_value = reinterpret_cast<void*>( return_value_z.get());
return_value_z.transferred();
break;
}
@ -1588,8 +1586,10 @@ void core_get_field_common( __inout sqlsrv_stmt* stmt, SQLUSMALLINT field_index,
THROW_CORE_ERROR(stmt, SQLSRV_ERROR_DATETIME_CONVERSION_FAILED);
}
*field_value = reinterpret_cast<void*>( return_value_z.get() );
field_value = reinterpret_cast<void*>( return_value_z.get());
return_value_z.transferred();
zend_string_free( Z_STR( field_value_temp_z ));
zend_string_free( Z_STR( function_z ));
break;
}
@ -1630,15 +1630,13 @@ void core_get_field_common( __inout sqlsrv_stmt* stmt, SQLUSMALLINT field_index,
// turn our stream into a zval to be returned
php_stream_to_zval( stream, return_value_z );
// mark this as our active stream
stmt->active_stream = return_value_z;
*field_value = reinterpret_cast<void*>( return_value_z.get() );
field_value = reinterpret_cast<void*>( return_value_z.get());
return_value_z.transferred();
break;
}
case SQLSRV_PHPTYPE_NULL:
*field_value = NULL;
field_value = NULL;
*field_len = 0;
break;
@ -1956,11 +1954,12 @@ void finalize_output_parameters( sqlsrv_stmt* stmt TSRMLS_DC )
bool converted = true;
HashTable* params_ht = Z_ARRVAL( stmt->output_params );
for( zend_hash_internal_pointer_reset( params_ht );
zend_hash_has_more_elements( params_ht ) == SUCCESS;
zend_hash_move_forward( params_ht ) ) {
sqlsrv_output_param* output_param = NULL;
core::sqlsrv_zend_hash_get_current_data_ptr(*stmt, params_ht, (void*&) output_param TSRMLS_CC);
zend_ulong index = -1;
zend_string* key = NULL;
void* output_param_temp = NULL;
ZEND_HASH_FOREACH_KEY_PTR( params_ht, index, key, output_param_temp ) {
sqlsrv_output_param* output_param = static_cast<sqlsrv_output_param*>( output_param_temp );
zval* value_z = Z_REFVAL_P( output_param->param_z );
switch( Z_TYPE_P( value_z )) {
case IS_STRING:
@ -2037,7 +2036,7 @@ void finalize_output_parameters( sqlsrv_stmt* stmt TSRMLS_DC )
break;
}
value_z = NULL;
}
} ZEND_HASH_FOREACH_END();
// empty the hash table since it's been processed
zend_hash_clean( Z_ARRVAL( stmt->output_params ));
@ -2045,7 +2044,7 @@ void finalize_output_parameters( sqlsrv_stmt* stmt TSRMLS_DC )
}
void get_field_as_string( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_phptype sqlsrv_php_type,
__out void** field_value, __out SQLLEN* field_len TSRMLS_DC )
__out void*& field_value, __out SQLLEN* field_len TSRMLS_DC )
{
SQLRETURN r;
SQLSMALLINT c_type;
@ -2102,7 +2101,7 @@ void get_field_as_string( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_ph
}
if( field_len_temp == SQL_NULL_DATA ) {
*field_value = NULL;
field_value = NULL;
sqlsrv_free( field_value_temp );
return;
}
@ -2167,7 +2166,7 @@ void get_field_as_string( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_ph
true /*handle_warning*/ TSRMLS_CC );
if( dummy_field_len == SQL_NULL_DATA ) {
*field_value = NULL;
field_value = NULL;
sqlsrv_free( field_value_temp );
return;
}
@ -2226,7 +2225,7 @@ void get_field_as_string( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_ph
}
if( field_len_temp == SQL_NULL_DATA ) {
*field_value = NULL;
field_value = NULL;
sqlsrv_free( field_value_temp );
return;
}
@ -2248,7 +2247,7 @@ void get_field_as_string( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_ph
return; // to eliminate a warning
}
*field_value = field_value_temp;
field_value = field_value_temp;
*field_len = field_len_temp;
// prevent a warning in debug mode about strings not being NULL terminated. Even though nulls are not necessary, the PHP
@ -2260,14 +2259,14 @@ void get_field_as_string( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_ph
catch( core::CoreException& ) {
*field_value = NULL;
field_value = NULL;
*field_len = 0;
sqlsrv_free( field_value_temp );
throw;
}
catch ( ... ) {
*field_value = NULL;
field_value = NULL;
*field_len = 0;
sqlsrv_free( field_value_temp );
throw;

View file

@ -32,11 +32,8 @@ int sqlsrv_stream_close( php_stream* stream, int /*close_handle*/ TSRMLS_DC )
// free the stream resources in the Zend engine
php_stream_free( stream, PHP_STREAM_FREE_RELEASE_STREAM );
// NULL out the stream zval and delete our reference count to it.
ZVAL_NULL( ss->stmt->active_stream );
// there is no active stream
ss->stmt->active_stream = NULL;
// UNDEF the stream zval and delete our reference count to it.
ZVAL_UNDEF( &( ss->stmt->active_stream ) );
sqlsrv_free( ss );
stream->abstract = NULL;

View file

@ -463,6 +463,8 @@ int pdo_sqlsrv_db_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC)
dbh->error_mode = prev_err_mode; // reset the error mode
g_henv_cp->invalidate();
return 0;
}
catch( ... ) {
@ -1310,25 +1312,20 @@ void validate_stmt_options( sqlsrv_context& ctx, zval* stmt_options, __inout Has
if( stmt_options ) {
HashTable* options_ht = Z_ARRVAL_P( stmt_options );
for( zend_hash_internal_pointer_reset( options_ht ); zend_hash_has_more_elements( options_ht ) == SUCCESS;
zend_hash_move_forward( options_ht )) {
int type = HASH_KEY_NON_EXISTENT;
zend_string *key = NULL;
size_t int_key = -1;
zval* data;
int result = 0;
zend_string *key = NULL;
zval* data = NULL;
type = zend_hash_get_current_key( options_ht, &key, &int_key);
ZEND_HASH_FOREACH_KEY_VAL( options_ht, int_key, key, data ) {
int type = HASH_KEY_NON_EXISTENT;
int result = 0;
type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG;
CHECK_CUSTOM_ERROR(( type != HASH_KEY_IS_LONG ), ctx, PDO_SQLSRV_ERROR_INVALID_STMT_OPTION ) {
throw core::CoreException();
}
core::sqlsrv_zend_hash_get_current_data( ctx, options_ht, data TSRMLS_CC );
add_stmt_option_key( ctx, int_key, pdo_stmt_options_ht, data TSRMLS_CC );
}
} ZEND_HASH_FOREACH_END();
}
}
catch( core::CoreException& ) {

View file

@ -743,9 +743,15 @@ int pdo_sqlsrv_stmt_get_col_data(pdo_stmt_t *stmt, int colno,
}
SQLSRV_PHPTYPE sqlsrv_phptype_out = SQLSRV_PHPTYPE_INVALID;
core_sqlsrv_get_field( driver_stmt, colno, sqlsrv_php_type, false, reinterpret_cast<void**>( ptr ),
core_sqlsrv_get_field( driver_stmt, colno, sqlsrv_php_type, false, *(reinterpret_cast<void**>(ptr)),
reinterpret_cast<SQLLEN*>( len ), true, &sqlsrv_phptype_out TSRMLS_CC );
zval* zval_ptr = reinterpret_cast<zval*>( sqlsrv_malloc( sizeof( zval )));
// if the current column is the last fetch column, finalize output params
if ( stmt->column_count == colno + 1 ) {
core_finalize_output_parameters( driver_stmt TSRMLS_CC );
}
zval* zval_ptr = ( zval* )( sqlsrv_malloc( sizeof( zval )));
*zval_ptr = convert_to_zval( sqlsrv_phptype_out, reinterpret_cast<void**>( ptr ), *len );
*ptr = reinterpret_cast<char*>( zval_ptr );

View file

@ -57,13 +57,13 @@ struct conn_char_set_func {
const char* encoding = Z_STRVAL_P( value );
size_t encoding_len = Z_STRLEN_P( value );
for( zend_hash_internal_pointer_reset( g_ss_encodings_ht );
zend_hash_has_more_elements( g_ss_encodings_ht ) == SUCCESS;
zend_hash_move_forward( g_ss_encodings_ht )) {
sqlsrv_encoding* ss_encoding = NULL;
core::sqlsrv_zend_hash_get_current_data_ptr( *conn, g_ss_encodings_ht,(void*&) ss_encoding TSRMLS_CC );
zend_ulong index = -1;
zend_string* key = NULL;
void* ss_encoding_temp = NULL;
ZEND_HASH_FOREACH_KEY_PTR( g_ss_encodings_ht, index, key, ss_encoding_temp ) {
sqlsrv_encoding* ss_encoding = reinterpret_cast<sqlsrv_encoding*>( ss_encoding_temp );
ss_encoding_temp = NULL;
if (!strnicmp( encoding, ss_encoding->iana, encoding_len )) {
if ( ss_encoding->not_for_connection ) {
@ -73,7 +73,7 @@ struct conn_char_set_func {
conn->set_encoding( static_cast<SQLSRV_ENCODING>(ss_encoding->code_page ));
return;
}
}
} ZEND_HASH_FOREACH_END();
THROW_SS_ERROR( conn, SS_SQLSRV_ERROR_CONNECT_ILLEGAL_ENCODING, encoding );
}
@ -1062,8 +1062,7 @@ PHP_FUNCTION( sqlsrv_query )
void free_stmt_resource( zval* stmt_z TSRMLS_DC )
{
int zr = zend_list_close(Z_RES_P(stmt_z));
if( zr == FAILURE ) {
if( FAILURE == zend_list_close( Z_RES_P( stmt_z ))) {
LOG(SEV_ERROR, "Failed to remove stmt resource %1!d!", Z_RES_HANDLE_P(stmt_z));
}
ZVAL_NULL( stmt_z );
@ -1086,21 +1085,18 @@ void sqlsrv_conn_close_stmts( ss_sqlsrv_conn* conn TSRMLS_DC )
// loop through the stmts hash table and destroy each stmt resource so we can close the
// ODBC connection
for( zend_hash_internal_pointer_reset( conn->stmts );
zend_hash_has_more_elements( conn->stmts ) == SUCCESS;
zend_hash_move_forward( conn->stmts )) {
zval* rsrc_ptr = NULL;
ZEND_HASH_FOREACH_VAL( conn->stmts, rsrc_ptr ) {
try {
// get the resource id for the next statement created with this connection
core::sqlsrv_zend_hash_get_current_data( *conn, conn->stmts, rsrc_ptr TSRMLS_CC );
int zr = ( rsrc_ptr ) != NULL ? SUCCESS : FAILURE;
CHECK_ZEND_ERROR( zr, *conn, SQLSRV_ERROR_ZEND_HASH ) {
throw core::CoreException();
}
}
catch( core::CoreException& ) {
DIE( "sqlsrv_conn_close_stmts: Failed to retrieve a statement resource from the connection" );
}
// see if the statement is still valid, and if not skip to the next one
// presumably this should never happen because if it's in the list, it should still be valid
// by virtue that a statement resource should remove itself from its connection when it is
@ -1113,7 +1109,6 @@ void sqlsrv_conn_close_stmts( ss_sqlsrv_conn* conn TSRMLS_DC )
" from the connection so this shouldn't be out of sync." );
continue;
}
// delete the statement by deleting it from Zend's resource list, which will force its destruction
stmt->conn = NULL;
@ -1124,7 +1119,7 @@ void sqlsrv_conn_close_stmts( ss_sqlsrv_conn* conn TSRMLS_DC )
catch( core::CoreException& ) {
LOG(SEV_ERROR, "Failed to remove statement resource %1!d! when closing the connection", Z_RES_HANDLE_P(rsrc_ptr));
}
}
} ZEND_HASH_FOREACH_END();
zend_hash_destroy( conn->stmts );
FREE_HASHTABLE( conn->stmts );
@ -1230,20 +1225,17 @@ void validate_stmt_options( sqlsrv_context& ctx, zval* stmt_options, __inout Has
if( stmt_options ) {
HashTable* options_ht = Z_ARRVAL_P( stmt_options );
for( zend_hash_internal_pointer_reset( options_ht ); zend_hash_has_more_elements( options_ht ) == SUCCESS;
zend_hash_move_forward( options_ht )) {
int type = HASH_KEY_NON_EXISTENT;
zend_string *key = NULL;
size_t key_len = 0;
zend_ulong int_key = -1;
zend_string *key = NULL;
zval* data = NULL;
ZEND_HASH_FOREACH_KEY_VAL( options_ht, int_key, key, data ) {
int type = HASH_KEY_NON_EXISTENT;
size_t key_len = 0;
zval* conn_opt = NULL;
int result = 0;
type = zend_hash_get_current_key(options_ht, &key, &int_key);
key_len = ZSTR_LEN( key ) + 1;
type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG;
if( type != HASH_KEY_IS_STRING ) {
std::ostringstream itoa;
@ -1252,9 +1244,8 @@ void validate_stmt_options( sqlsrv_context& ctx, zval* stmt_options, __inout Has
throw core::CoreException();
}
}
core::sqlsrv_zend_hash_get_current_data( ctx, options_ht, data TSRMLS_CC );
add_stmt_option_key( ctx, key, key_len, ss_stmt_options_ht, data TSRMLS_CC );
}
} ZEND_HASH_FOREACH_END();
}
}
catch( core::CoreException& ) {
@ -1274,23 +1265,21 @@ void validate_conn_options( sqlsrv_context& ctx, zval* user_options_z, __out cha
if( user_options_z ) {
HashTable* options_ht = Z_ARRVAL_P( user_options_z );
for( zend_hash_internal_pointer_reset( options_ht ); zend_hash_has_more_elements( options_ht ) == SUCCESS;
zend_hash_move_forward( options_ht )) {
int type = HASH_KEY_NON_EXISTENT;
zend_string *key = NULL;
zend_ulong int_key = -1;
zend_string *key = NULL;
zval* data = NULL;
ZEND_HASH_FOREACH_KEY_VAL( options_ht, int_key, key, data ) {
int type = HASH_KEY_NON_EXISTENT;
type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG;
type = zend_hash_get_current_key(options_ht, &key, &int_key);
CHECK_CUSTOM_ERROR(( Z_TYPE_P( data ) == IS_NULL || Z_TYPE_P( data ) == IS_UNDEF ), ctx, SS_SQLSRV_ERROR_INVALID_OPTION, key) {
throw ss::SSException();
}
CHECK_CUSTOM_ERROR(( type != HASH_KEY_IS_STRING ), ctx, SS_SQLSRV_ERROR_INVALID_CONNECTION_KEY ) {
throw ss::SSException();
}
core::sqlsrv_zend_hash_get_current_data( ctx, options_ht, data TSRMLS_CC );
// Length of the key string does not include the null terminator in PHP7, +1 has to be added
size_t key_len = ZSTR_LEN(key) + 1;
if( key_len == sizeof(SSConnOptionNames::UID) && !stricmp(ZSTR_VAL(key), SSConnOptionNames::UID )) {
@ -1306,7 +1295,7 @@ void validate_conn_options( sqlsrv_context& ctx, zval* user_options_z, __out cha
::add_conn_option_key( ctx, key, key_len, ss_conn_options_ht, data TSRMLS_CC );
}
}
} ZEND_HASH_FOREACH_END();
}
}
catch( core::CoreException& ) {

View file

@ -42,7 +42,7 @@ const int INFO_BUFFER_LEN = 256;
const char* PROCESSOR_ARCH[] = { "x86", "x64", "ia64" };
// ODBC driver name.
const char CONNECTION_STRING_DRIVER_NAME[] = "Driver={ODBC Driver 11 for SQL Server};";
const char* CONNECTION_STRING_DRIVER_NAME[] = {"Driver={ODBC Driver 13 for SQL Server};", "Driver={ODBC Driver 11 for SQL Server};"};
// default options if only the server is specified
const char CONNECTION_STRING_DEFAULT_OPTIONS[] = "Mars_Connection={Yes}";
@ -124,7 +124,8 @@ sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_
conn = conn_factory( temp_conn_h, err, driver TSRMLS_CC );
conn->set_func( driver_func );
for ( std::size_t i = DRIVER_VERSION::MIN; i <= DRIVER_VERSION::MAX; ++i ) {
conn_str = CONNECTION_STRING_DRIVER_NAME[i];
build_connection_string_and_set_conn_attr( conn, server, uid, pwd, options_ht, valid_conn_opts, driver,
conn_str TSRMLS_CC );
@ -151,11 +152,20 @@ sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_
SQLCHAR state[SQL_SQLSTATE_BUFSIZE];
SQLSMALLINT len;
SQLRETURN r = SQLGetDiagField( SQL_HANDLE_DBC, conn->handle(), 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len );
bool missing_driver_error = ( SQL_SUCCEEDED( r ) && state[0] == 'I' && state[1] == 'M' && state[2] == '0' && state[3] == '0' &&
state[4] == '2' );
// if it's a IM002, meaning that the correct ODBC driver is not installed
CHECK_CUSTOM_ERROR( SQL_SUCCEEDED( r ) && state[0] == 'I' && state[1] == 'M' && state[2] == '0' && state[3] == '0' &&
state[4] == '2', conn, SQLSRV_ERROR_DRIVER_NOT_INSTALLED, get_processor_arch() ) {
CHECK_CUSTOM_ERROR( missing_driver_error && ( i == DRIVER_VERSION::MAX ), conn, SQLSRV_ERROR_DRIVER_NOT_INSTALLED, get_processor_arch()) {
throw core::CoreException();
}
if ( !missing_driver_error ) {
break;
}
} else {
conn->driver_version = static_cast<DRIVER_VERSION>( i );
break;
}
}
CHECK_SQL_ERROR( r, conn ) {
throw core::CoreException();
@ -533,8 +543,6 @@ void build_connection_string_and_set_conn_attr( sqlsrv_conn* conn, const char* s
try {
connection_string = CONNECTION_STRING_DRIVER_NAME;
// Add the server name
common_conn_str_append_func( ODBCConnOptions::SERVER, server, strlen( server ), connection_string TSRMLS_CC );
@ -586,22 +594,17 @@ void build_connection_string_and_set_conn_attr( sqlsrv_conn* conn, const char* s
}
}
for( zend_hash_internal_pointer_reset( options );
zend_hash_has_more_elements( options ) == SUCCESS;
zend_hash_move_forward( options )) {
int type = HASH_KEY_NON_EXISTENT;
zend_string *key = NULL;
zend_ulong index = 0;
zend_ulong index = -1;
zval* data = NULL;
type = zend_hash_get_current_key( options, &key, &index );
ZEND_HASH_FOREACH_KEY_VAL( options, index, key, data ) {
int type = HASH_KEY_NON_EXISTENT;
type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG;
// The driver layer should ensure a valid key.
DEBUG_SQLSRV_ASSERT(( type == HASH_KEY_IS_LONG ), "build_connection_string_and_set_conn_attr: invalid connection option key type." );
core::sqlsrv_zend_hash_get_current_data( *conn, options, data TSRMLS_CC );
conn_opt = get_connection_option( conn, index, valid_conn_opts TSRMLS_CC );
if( index == SQLSRV_CONN_OPTION_MARS ) {
@ -609,7 +612,7 @@ void build_connection_string_and_set_conn_attr( sqlsrv_conn* conn, const char* s
}
conn_opt->func( conn_opt, data, conn, connection_string TSRMLS_CC );
}
} ZEND_HASH_FOREACH_END();
// MARS on if not explicitly turned off
if( !mars_mentioned ) {
@ -755,7 +758,7 @@ size_t core_str_zval_is_true( zval* value_z )
}
// save adjustments to the value made by stripping whitespace at the end
core::sqlsrv_zval_stringl( value_z, value_in, val_len);
Z_STRLEN_P( value_z ) = val_len;
const char VALID_TRUE_VALUE_1[] = "true";
const char VALID_TRUE_VALUE_2[] = "1";

View file

@ -970,6 +970,14 @@ enum SERVER_VERSION {
SERVER_VERSION_2008, // use this for anything 2008 or later
};
// supported driver versions.
enum DRIVER_VERSION : size_t {
MIN = 0,
ODBC_DRIVER_13 = MIN,
ODBC_DRIVER_11 = 1,
MAX = ODBC_DRIVER_11,
};
// forward decl
struct sqlsrv_stmt;
struct stmt_option;
@ -981,6 +989,8 @@ struct sqlsrv_conn : public sqlsrv_context {
// instance variables
SERVER_VERSION server_version; // version of the server that we're connected to
DRIVER_VERSION driver_version;
// initialize with default values
sqlsrv_conn( SQLHANDLE h, error_callback e, void* drv, SQLSRV_ENCODING encoding TSRMLS_DC ) :
sqlsrv_context( h, SQL_HANDLE_DBC, e, drv, encoding )
@ -1258,7 +1268,7 @@ struct sqlsrv_stmt : public sqlsrv_context {
unsigned int current_stream_read; // # of bytes read so far. (if we read an empty PHP stream, we send an empty string
// to the server)
zval field_cache; // cache for a single row of fields, to allow multiple and out of order retrievals
zval* active_stream; // the currently active stream reading data from the database
zval active_stream; // the currently active stream reading data from the database
sqlsrv_stmt( sqlsrv_conn* c, SQLHANDLE handle, error_callback e, void* drv TSRMLS_DC );
virtual ~sqlsrv_stmt( void );
@ -1313,7 +1323,7 @@ void core_sqlsrv_execute( sqlsrv_stmt* stmt TSRMLS_DC, const char* sql = NULL, i
field_meta_data* core_sqlsrv_field_metadata( sqlsrv_stmt* stmt, SQLSMALLINT colno TSRMLS_DC );
bool core_sqlsrv_fetch( sqlsrv_stmt* stmt, SQLSMALLINT fetch_orientation, SQLULEN fetch_offset TSRMLS_DC );
void core_sqlsrv_get_field(sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_phptype sqlsrv_phptype, bool prefer_string,
__out void** field_value, __out SQLLEN* field_length, bool cache_field,
__out void*& field_value, __out SQLLEN* field_length, bool cache_field,
__out SQLSRV_PHPTYPE *sqlsrv_php_type_out TSRMLS_DC);
bool core_sqlsrv_has_any_result( sqlsrv_stmt* stmt TSRMLS_DC );
void core_sqlsrv_next_result( sqlsrv_stmt* stmt TSRMLS_DC, bool finalize_output_params = true, bool throw_on_errors = true );
@ -1325,6 +1335,7 @@ void core_sqlsrv_set_send_at_exec( sqlsrv_stmt* stmt, zval* value_z TSRMLS_DC );
bool core_sqlsrv_send_stream_packet( sqlsrv_stmt* stmt TSRMLS_DC );
void core_sqlsrv_set_buffered_query_limit( sqlsrv_stmt* stmt, zval* value_z TSRMLS_DC );
void core_sqlsrv_set_buffered_query_limit( sqlsrv_stmt* stmt, SQLLEN limit TSRMLS_DC );
void core_finalize_output_parameters(sqlsrv_stmt* stmt TSRMLS_DC);
//*********************************************************************************************************************************
@ -1564,8 +1575,8 @@ enum SQLSRV_ERROR_CODES {
};
// the message returned by ODBC Driver 11 for SQL Server
const char CONNECTION_BUSY_ODBC_ERROR[] = "[Microsoft][ODBC Driver 11 for SQL Server]Connection is busy with results for "
"another command";
static const char* CONNECTION_BUSY_ODBC_ERROR[] = { "[Microsoft][ODBC Driver 13 for SQL Server]Connection is busy with results for another command",
"[Microsoft][ODBC Driver 11 for SQL Server]Connection is busy with results for another command" };
// SQLSTATE for all internal errors
extern SQLCHAR IMSSP[];
@ -1742,9 +1753,9 @@ namespace core {
throw CoreException();
}
if(( len == sizeof( CONNECTION_BUSY_ODBC_ERROR ) - 1 ) &&
!strcmp( reinterpret_cast<const char*>( err_msg ), CONNECTION_BUSY_ODBC_ERROR )) {
std::size_t driver_version = stmt->conn->driver_version;
if(( len == sizeof( CONNECTION_BUSY_ODBC_ERROR[driver_version] ) - 1 ) &&
!strcmp( reinterpret_cast<const char*>( err_msg ), CONNECTION_BUSY_ODBC_ERROR[driver_version] )) {
THROW_CORE_ERROR( stmt, SQLSRV_ERROR_MARS_OFF );
}

View file

@ -81,7 +81,7 @@ size_t calc_utf8_missing( sqlsrv_stmt* stmt, const char* buffer, size_t buffer_e
bool check_for_next_stream_parameter( sqlsrv_stmt* stmt TSRMLS_DC );
bool convert_input_param_to_utf16( zval* input_param_z, zval* convert_param_z );
void core_get_field_common(__inout sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_phptype
sqlsrv_php_type, __out void** field_value, __out SQLLEN* field_len TSRMLS_DC );
sqlsrv_php_type, __out void*& field_value, __out SQLLEN* field_len TSRMLS_DC);
// returns the ODBC C type constant that matches the PHP type and encoding given
SQLSMALLINT default_c_type( sqlsrv_stmt* stmt, SQLULEN paramno, zval const* param_z, SQLSRV_ENCODING encoding TSRMLS_DC );
void default_sql_size_and_scale( sqlsrv_stmt* stmt, unsigned int paramno, zval* param_z, SQLSRV_ENCODING encoding,
@ -92,7 +92,7 @@ void default_sql_type( sqlsrv_stmt* stmt, SQLULEN paramno, zval* param_z, SQLSRV
void field_cache_dtor( zval* data_z );
void finalize_output_parameters( sqlsrv_stmt* stmt TSRMLS_DC );
void get_field_as_string( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_phptype sqlsrv_php_type,
__out void** field_value, __out SQLLEN* field_len TSRMLS_DC );
__out void*& field_value, __out SQLLEN* field_len TSRMLS_DC );
stmt_option const* get_stmt_option( sqlsrv_conn const* conn, zend_ulong key, const stmt_option stmt_opts[] TSRMLS_DC );
bool is_valid_sqlsrv_phptype( sqlsrv_phptype type );
// assure there is enough space for the output parameter string
@ -127,9 +127,9 @@ sqlsrv_stmt::sqlsrv_stmt( sqlsrv_conn* c, SQLHANDLE handle, error_callback e, vo
current_stream( NULL, SQLSRV_ENCODING_DEFAULT ),
current_stream_read( 0 ),
query_timeout( QUERY_TIMEOUT_INVALID ),
buffered_query_limit( sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_INVALID ),
active_stream( NULL )
buffered_query_limit( sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_INVALID )
{
ZVAL_UNDEF( &active_stream );
// initialize the input string parameters array (which holds zvals)
core::sqlsrv_array_init( *conn, &param_input_strings TSRMLS_CC );
@ -152,7 +152,7 @@ sqlsrv_stmt::sqlsrv_stmt( sqlsrv_conn* c, SQLHANDLE handle, error_callback e, vo
// desctructor for sqlsrv statement.
sqlsrv_stmt::~sqlsrv_stmt( void )
{
if( active_stream ) {
if( Z_TYPE( active_stream ) != IS_UNDEF ) {
TSRMLS_FETCH();
close_active_stream( this TSRMLS_CC );
}
@ -248,21 +248,17 @@ sqlsrv_stmt* core_sqlsrv_create_stmt( sqlsrv_conn* conn, driver_stmt_factory stm
// process the options array given to core_sqlsrv_prepare.
if( options_ht && zend_hash_num_elements( options_ht ) > 0 ) {
for( zend_hash_internal_pointer_reset( options_ht );
zend_hash_has_more_elements( options_ht ) == SUCCESS;
zend_hash_move_forward( options_ht )) {
zend_string *key = NULL;
zend_ulong index = -1;
zend_string *key = NULL;
zval* value_z = NULL;
int type = zend_hash_get_current_key( options_ht, &key, &index);
ZEND_HASH_FOREACH_KEY_VAL( options_ht, index, key, value_z ) {
int type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG;
// The driver layer should ensure a valid key.
DEBUG_SQLSRV_ASSERT(( type == HASH_KEY_IS_LONG ), "allocate_stmt: Invalid statment option key provided." );
core::sqlsrv_zend_hash_get_current_data( *(stmt->conn), options_ht, value_z TSRMLS_CC );
const stmt_option* stmt_opt = get_stmt_option( stmt->conn, index, valid_stmt_opts TSRMLS_CC );
// if the key didn't match, then return the error to the script.
@ -271,9 +267,7 @@ sqlsrv_stmt* core_sqlsrv_create_stmt( sqlsrv_conn* conn, driver_stmt_factory stm
// perform the actions the statement option needs done.
(*stmt_opt->func)( stmt, stmt_opt, value_z TSRMLS_CC );
}
zend_hash_internal_pointer_end( options_ht );
} ZEND_HASH_FOREACH_END();
}
sqlsrv_stmt* return_stmt = stmt;
@ -658,13 +652,13 @@ void core_sqlsrv_bind_param(sqlsrv_stmt* stmt, SQLUSMALLINT param_num, SQLSMALLI
void core_sqlsrv_execute( sqlsrv_stmt* stmt TSRMLS_DC, const char* sql, int sql_len )
{
SQLRETURN r;
try {
// close the stream to release the resource
close_active_stream( stmt TSRMLS_CC );
SQLRETURN r;
if( sql ) {
sqlsrv_malloc_auto_ptr<wchar_t> wsql_string;
@ -705,8 +699,9 @@ void core_sqlsrv_execute( sqlsrv_stmt* stmt TSRMLS_DC, const char* sql, int sql_
finalize_output_parameters( stmt TSRMLS_CC );
}
// stream parameters are sent, clean the Hashtable
if ( stmt->send_streams_at_exec ) {
zend_hash_clean( Z_ARRVAL( stmt->param_streams ));
}
}
catch( core::CoreException& e ) {
@ -869,7 +864,7 @@ field_meta_data* core_sqlsrv_field_metadata( sqlsrv_stmt* stmt, SQLSMALLINT coln
// Nothing, excpetion thrown if an error occurs
void core_sqlsrv_get_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_phptype sqlsrv_php_type_in, bool prefer_string,
__out void** field_value, __out SQLLEN* field_len, bool cache_field,
__out void*& field_value, __out SQLLEN* field_len, bool cache_field,
__out SQLSRV_PHPTYPE *sqlsrv_php_type_out TSRMLS_DC)
{
try {
@ -879,20 +874,20 @@ void core_sqlsrv_get_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_
// if the field has been retrieved before, return the previous result
field_cache* cached = NULL;
if (NULL != (cached = reinterpret_cast<field_cache*>(zend_hash_index_find_ptr(Z_ARRVAL(stmt->field_cache), static_cast<zend_ulong>(field_index))))) {
if (NULL != ( cached = static_cast<field_cache*>( zend_hash_index_find_ptr( Z_ARRVAL( stmt->field_cache ), static_cast<zend_ulong>( field_index ))))) {
// the field value is NULL
if( cached->value == NULL ) {
*field_value = NULL;
field_value = NULL;
*field_len = 0;
if( sqlsrv_php_type_out ) { *sqlsrv_php_type_out = SQLSRV_PHPTYPE_NULL; }
}
else {
*field_value = sqlsrv_malloc( cached->len, sizeof( char ), 1 );
memcpy( *field_value, cached->value, cached->len );
field_value = sqlsrv_malloc( cached->len, sizeof( char ), 1 );
memcpy( field_value, cached->value, cached->len );
if( cached->type.typeinfo.type == SQLSRV_PHPTYPE_STRING) {
// prevent the 'string not null terminated' warning
reinterpret_cast<char*>( *field_value )[ cached->len ] = '\0';
reinterpret_cast<char*>( field_value )[ cached->len ] = '\0';
}
*field_len = cached->len;
if( sqlsrv_php_type_out) { *sqlsrv_php_type_out = static_cast<SQLSRV_PHPTYPE>(cached->type.typeinfo.type); }
@ -921,9 +916,9 @@ void core_sqlsrv_get_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_
core_sqlsrv_get_field( stmt, i, invalid, prefer_string, field_value, field_len, cache_field,
sqlsrv_php_type_out TSRMLS_CC );
// delete the value returned since we only want it cached, not the actual value
if( *field_value ) {
efree( *field_value );
*field_value = NULL;
if( field_value ) {
efree( field_value );
field_value = NULL;
*field_len = 0;
}
}
@ -955,7 +950,7 @@ void core_sqlsrv_get_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_
// if the user wants us to cache the field, we'll do it
if( cache_field ) {
field_cache cache( *field_value, *field_len, sqlsrv_php_type );
field_cache cache( field_value, *field_len, sqlsrv_php_type );
core::sqlsrv_zend_hash_index_update_mem( *stmt, Z_ARRVAL( stmt->field_cache ), field_index, &cache, sizeof(field_cache) TSRMLS_CC );
}
}
@ -1304,6 +1299,9 @@ bool core_sqlsrv_send_stream_packet( sqlsrv_stmt* stmt TSRMLS_DC )
return true;
}
void core_finalize_output_parameters(sqlsrv_stmt* stmt TSRMLS_DC) {
finalize_output_parameters(stmt TSRMLS_CC);
}
void stmt_option_functor::operator()( sqlsrv_stmt* /*stmt*/, stmt_option const* /*opt*/, zval* /*value_z*/ TSRMLS_DC )
{
@ -1334,20 +1332,20 @@ void stmt_option_buffered_query_limit:: operator()( sqlsrv_stmt* stmt, stmt_opti
void close_active_stream( __inout sqlsrv_stmt* stmt TSRMLS_DC )
{
// if there is no active stream, return
if( stmt->active_stream == NULL ) {
if( Z_TYPE( stmt->active_stream ) == IS_UNDEF ) {
return;
}
php_stream* stream = NULL;
// we use no verify since verify would return immediately and we want to assert, not return.
php_stream_from_zval_no_verify( stream, stmt->active_stream );
php_stream_from_zval_no_verify( stream, &( stmt->active_stream ));
SQLSRV_ASSERT(( stream != NULL ), "close_active_stream: Unknown resource type as our active stream." );
php_stream_close( stream ); // this will NULL out the active stream in the statement. We don't check for errors here.
SQLSRV_ASSERT( stmt->active_stream == NULL, "close_active_stream: Active stream not closed." );
SQLSRV_ASSERT( Z_TYPE( stmt->active_stream ) == IS_UNDEF, "close_active_stream: Active stream not closed." );
}
@ -1471,7 +1469,7 @@ size_t calc_utf8_missing( sqlsrv_stmt* stmt, const char* buffer, size_t buffer_e
// the driver layer would have to calculate size of the field_value
// to decide the amount of memory allocation.
void core_get_field_common( __inout sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_phptype
sqlsrv_php_type, __out void** field_value, __out SQLLEN* field_len TSRMLS_DC )
sqlsrv_php_type, __out void*& field_value, __out SQLLEN* field_len TSRMLS_DC )
{
try {
@ -1507,11 +1505,11 @@ void core_get_field_common( __inout sqlsrv_stmt* stmt, SQLUSMALLINT field_index,
}
if( *field_len == SQL_NULL_DATA ) {
*field_value = NULL;
field_value = NULL;
break;
}
*field_value = field_value_temp;
field_value = field_value_temp;
field_value_temp.transferred();
break;
}
@ -1533,11 +1531,11 @@ void core_get_field_common( __inout sqlsrv_stmt* stmt, SQLUSMALLINT field_index,
}
if( *field_len == SQL_NULL_DATA ) {
*field_value = NULL;
field_value = NULL;
break;
}
*field_value = field_value_temp;
field_value = field_value_temp;
field_value_temp.transferred();
break;
}
@ -1573,7 +1571,7 @@ void core_get_field_common( __inout sqlsrv_stmt* stmt, SQLUSMALLINT field_index,
if( *field_len == SQL_NULL_DATA ) {
ZVAL_NULL( return_value_z );
*field_value = reinterpret_cast<void*>( return_value_z.get() );
field_value = reinterpret_cast<void*>( return_value_z.get());
return_value_z.transferred();
break;
}
@ -1588,8 +1586,10 @@ void core_get_field_common( __inout sqlsrv_stmt* stmt, SQLUSMALLINT field_index,
THROW_CORE_ERROR(stmt, SQLSRV_ERROR_DATETIME_CONVERSION_FAILED);
}
*field_value = reinterpret_cast<void*>( return_value_z.get() );
field_value = reinterpret_cast<void*>( return_value_z.get());
return_value_z.transferred();
zend_string_free( Z_STR( field_value_temp_z ));
zend_string_free( Z_STR( function_z ));
break;
}
@ -1630,15 +1630,13 @@ void core_get_field_common( __inout sqlsrv_stmt* stmt, SQLUSMALLINT field_index,
// turn our stream into a zval to be returned
php_stream_to_zval( stream, return_value_z );
// mark this as our active stream
stmt->active_stream = return_value_z;
*field_value = reinterpret_cast<void*>( return_value_z.get() );
field_value = reinterpret_cast<void*>( return_value_z.get());
return_value_z.transferred();
break;
}
case SQLSRV_PHPTYPE_NULL:
*field_value = NULL;
field_value = NULL;
*field_len = 0;
break;
@ -1956,11 +1954,12 @@ void finalize_output_parameters( sqlsrv_stmt* stmt TSRMLS_DC )
bool converted = true;
HashTable* params_ht = Z_ARRVAL( stmt->output_params );
for( zend_hash_internal_pointer_reset( params_ht );
zend_hash_has_more_elements( params_ht ) == SUCCESS;
zend_hash_move_forward( params_ht ) ) {
sqlsrv_output_param* output_param = NULL;
core::sqlsrv_zend_hash_get_current_data_ptr(*stmt, params_ht, (void*&) output_param TSRMLS_CC);
zend_ulong index = -1;
zend_string* key = NULL;
void* output_param_temp = NULL;
ZEND_HASH_FOREACH_KEY_PTR( params_ht, index, key, output_param_temp ) {
sqlsrv_output_param* output_param = static_cast<sqlsrv_output_param*>( output_param_temp );
zval* value_z = Z_REFVAL_P( output_param->param_z );
switch( Z_TYPE_P( value_z )) {
case IS_STRING:
@ -2037,7 +2036,7 @@ void finalize_output_parameters( sqlsrv_stmt* stmt TSRMLS_DC )
break;
}
value_z = NULL;
}
} ZEND_HASH_FOREACH_END();
// empty the hash table since it's been processed
zend_hash_clean( Z_ARRVAL( stmt->output_params ));
@ -2045,7 +2044,7 @@ void finalize_output_parameters( sqlsrv_stmt* stmt TSRMLS_DC )
}
void get_field_as_string( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_phptype sqlsrv_php_type,
__out void** field_value, __out SQLLEN* field_len TSRMLS_DC )
__out void*& field_value, __out SQLLEN* field_len TSRMLS_DC )
{
SQLRETURN r;
SQLSMALLINT c_type;
@ -2102,7 +2101,7 @@ void get_field_as_string( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_ph
}
if( field_len_temp == SQL_NULL_DATA ) {
*field_value = NULL;
field_value = NULL;
sqlsrv_free( field_value_temp );
return;
}
@ -2167,7 +2166,7 @@ void get_field_as_string( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_ph
true /*handle_warning*/ TSRMLS_CC );
if( dummy_field_len == SQL_NULL_DATA ) {
*field_value = NULL;
field_value = NULL;
sqlsrv_free( field_value_temp );
return;
}
@ -2226,7 +2225,7 @@ void get_field_as_string( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_ph
}
if( field_len_temp == SQL_NULL_DATA ) {
*field_value = NULL;
field_value = NULL;
sqlsrv_free( field_value_temp );
return;
}
@ -2248,7 +2247,7 @@ void get_field_as_string( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_ph
return; // to eliminate a warning
}
*field_value = field_value_temp;
field_value = field_value_temp;
*field_len = field_len_temp;
// prevent a warning in debug mode about strings not being NULL terminated. Even though nulls are not necessary, the PHP
@ -2260,14 +2259,14 @@ void get_field_as_string( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_ph
catch( core::CoreException& ) {
*field_value = NULL;
field_value = NULL;
*field_len = 0;
sqlsrv_free( field_value_temp );
throw;
}
catch ( ... ) {
*field_value = NULL;
field_value = NULL;
*field_len = 0;
sqlsrv_free( field_value_temp );
throw;

View file

@ -32,11 +32,8 @@ int sqlsrv_stream_close( php_stream* stream, int /*close_handle*/ TSRMLS_DC )
// free the stream resources in the Zend engine
php_stream_free( stream, PHP_STREAM_FREE_RELEASE_STREAM );
// NULL out the stream zval and delete our reference count to it.
ZVAL_NULL( ss->stmt->active_stream );
// there is no active stream
ss->stmt->active_stream = NULL;
// UNDEF the stream zval and delete our reference count to it.
ZVAL_UNDEF( &( ss->stmt->active_stream ) );
sqlsrv_free( ss );
stream->abstract = NULL;

View file

@ -531,7 +531,6 @@ PHP_MINIT_FUNCTION(sqlsrv)
}
try {
// retrieve the handles for the environments
core_sqlsrv_minit( &g_henv_cp, &g_henv_ncp, ss_error_handler, "PHP_MINIT_FUNCTION for sqlsrv" TSRMLS_CC );
}
@ -551,13 +550,13 @@ PHP_MINIT_FUNCTION(sqlsrv)
// called by Zend for each parameter in the g_ss_warnings_to_ignore_ht and g_ss_errors_ht hash table when it is destroyed
void sqlsrv_error_const_dtor( zval* elem ) {
sqlsrv_error_const* error_to_ignore = reinterpret_cast<sqlsrv_error_const*>( Z_PTR_P(elem) );
sqlsrv_error_const* error_to_ignore = static_cast<sqlsrv_error_const*>( Z_PTR_P(elem) );
pefree(error_to_ignore, 1);
}
// called by Zend for each parameter in the g_ss_encodings_ht hash table when it is destroyed
void sqlsrv_encoding_dtor( zval* elem ) {
sqlsrv_encoding* sql_enc = reinterpret_cast<sqlsrv_encoding*>( Z_PTR_P(elem) );
sqlsrv_encoding* sql_enc = static_cast<sqlsrv_encoding*>( Z_PTR_P(elem) );
pefree(sql_enc, 1);
}
@ -611,10 +610,8 @@ PHP_RINIT_FUNCTION(sqlsrv)
#endif
SQLSRV_G( warnings_return_as_errors ) = true;
SQLSRV_G( errors ) = (zval *)sqlsrv_malloc(sizeof(zval));
ZVAL_NULL(SQLSRV_G(errors));
SQLSRV_G( warnings ) = (zval *)sqlsrv_malloc(sizeof(zval));
ZVAL_NULL(SQLSRV_G(warnings));
ZVAL_NULL( &SQLSRV_G( errors ));
ZVAL_NULL( &SQLSRV_G( warnings ));
LOG_FUNCTION( "PHP_RINIT for php_sqlsrv" );
@ -646,8 +643,8 @@ PHP_RSHUTDOWN_FUNCTION(sqlsrv)
reset_errors( TSRMLS_C );
// TODO - destruction
zval_ptr_dtor( SQLSRV_G( errors ));
zval_ptr_dtor( SQLSRV_G( warnings ));
zval_ptr_dtor( &SQLSRV_G( errors ));
zval_ptr_dtor( &SQLSRV_G( warnings ));
return SUCCESS;
}

View file

@ -10,28 +10,30 @@
#define __msodbcsql_h__
#if !defined(SQLODBC_VER)
#define SQLODBC_VER 1100
#define SQLODBC_VER 1300
#endif
#if SQLODBC_VER >= 1100
#if SQLODBC_VER >= 1300
#define SQLODBC_PRODUCT_NAME_FULL_VER_ANSI "Microsoft ODBC Driver 11 for SQL Server"
#define SQLODBC_PRODUCT_NAME_FULL_VER_ANSI "Microsoft ODBC Driver 13 for SQL Server"
#define SQLODBC_PRODUCT_NAME_FULL_ANSI "Microsoft ODBC Driver for SQL Server"
#define SQLODBC_PRODUCT_NAME_SHORT_VER_ANSI "ODBC Driver 11 for SQL Server"
#define SQLODBC_PRODUCT_NAME_SHORT_VER_ANSI "ODBC Driver 13 for SQL Server"
#define SQLODBC_PRODUCT_NAME_SHORT_ANSI "ODBC Driver for SQL Server"
#define SQLODBC_FILE_NAME_ANSI "msodbcsql"
#define SQLODBC_FILE_NAME_VER_ANSI "msodbcsql11"
#define SQLODBC_FILE_NAME_FULL_ANSI "msodbcsql11.dll"
#define SQLODBC_FILE_NAME_VER_ANSI "msodbcsql13"
#define SQLODBC_FILE_NAME_FULL_ANSI "msodbcsql13.dll"
#define SQLODBC_PRODUCT_NAME_FULL_VER_UNICODE L"Microsoft ODBC Driver 11 for SQL Server"
#define SQLODBC_PRODUCT_NAME_FULL_VER_UNICODE L"Microsoft ODBC Driver 13 for SQL Server"
#define SQLODBC_PRODUCT_NAME_FULL_UNICODE L"Microsoft ODBC Driver for SQL Server"
#define SQLODBC_PRODUCT_NAME_SHORT_VER_UNICODE L"ODBC Driver 11 for SQL Server"
#define SQLODBC_PRODUCT_NAME_SHORT_VER_UNICODE L"ODBC Driver 13 for SQL Server"
#define SQLODBC_PRODUCT_NAME_SHORT_UNICODE L"ODBC Driver for SQL Server"
#define SQLODBC_FILE_NAME_UNICODE L"msodbcsql"
#define SQLODBC_FILE_NAME_VER_UNICODE L"msodbcsql11"
#define SQLODBC_FILE_NAME_FULL_UNICODE L"msodbcsql11.dll"
#define SQLODBC_FILE_NAME_VER_UNICODE L"msodbcsql13"
#define SQLODBC_FILE_NAME_FULL_UNICODE L"msodbcsql13.dll"
// define the character type agnostic constants
#if defined(_UNICODE) || defined(UNICODE)
@ -203,6 +205,10 @@ extern "C" {
#define SQL_COPT_SS_INTEGRATED_AUTHENTICATION_METHOD (SQL_COPT_SS_BASE+31) // The integrated authentication method used for the connection
#define SQL_COPT_SS_MUTUALLY_AUTHENTICATED (SQL_COPT_SS_BASE+32) // Used to decide if the connection is mutually authenticated
#define SQL_COPT_SS_CLIENT_CONNECTION_ID (SQL_COPT_SS_BASE+33) // Post connection attribute used to get the ConnectionID
#define SQL_COPT_SS_CLIENT_CERTIFICATE (SQL_COPT_SS_BASE+36) // Client certificate
#define SQL_COPT_SS_CLIENT_CERTIFICATE_FALLBACK (SQL_COPT_SS_BASE+37) // Client certificate fallback
// Define old names
#define SQL_REMOTE_PWD SQL_COPT_SS_REMOTE_PWD
#define SQL_USE_PROCEDURE_FOR_PREPARE SQL_COPT_SS_USE_PROC_FOR_PREP
@ -225,7 +231,8 @@ extern "C" {
#define SQL_SOPT_SS_QUERYNOTIFICATION_OPTIONS (SQL_SOPT_SS_BASE+10)// SQL service broker name
#define SQL_SOPT_SS_PARAM_FOCUS (SQL_SOPT_SS_BASE+11)// Direct subsequent calls to parameter related methods to set properties on constituent columns/parameters of container types
#define SQL_SOPT_SS_NAME_SCOPE (SQL_SOPT_SS_BASE+12)// Sets name scope for subsequent catalog function calls
#define SQL_SOPT_SS_MAX_USED SQL_SOPT_SS_NAME_SCOPE
#define SQL_SOPT_SS_COLUMN_ENCRYPTION (SQL_SOPT_SS_BASE+13)// Sets the column encryption mode
#define SQL_SOPT_SS_MAX_USED SQL_SOPT_SS_COLUMN_ENCRYPTION
// Define old names
#define SQL_TEXTPTR_LOGGING SQL_SOPT_SS_TEXTPTR_LOGGING
#define SQL_COPT_SS_BASE_EX 1240
@ -237,7 +244,13 @@ extern "C" {
#define SQL_COPT_SS_RESET_CONNECTION (SQL_COPT_SS_BASE_EX+6) // When this option is set, we will perform connection reset on next packet
#define SQL_COPT_SS_APPLICATION_INTENT (SQL_COPT_SS_BASE_EX+7) // Application Intent
#define SQL_COPT_SS_MULTISUBNET_FAILOVER (SQL_COPT_SS_BASE_EX+8) // Multi-subnet Failover
#define SQL_COPT_SS_EX_MAX_USED SQL_COPT_SS_MULTISUBNET_FAILOVER
#define SQL_COPT_SS_TNIR (SQL_COPT_SS_BASE_EX+9) // Transparent Network IP Resolution
#define SQL_COPT_SS_COLUMN_ENCRYPTION (SQL_COPT_SS_BASE_EX+10) // Always Encrypted Enabled or Disabled
#define SQL_COPT_SS_AEKEYSTOREPROVIDER (SQL_COPT_SS_BASE_EX+11) // Used to load a keystore provider DLL
#define SQL_COPT_SS_AEKEYSTOREDATA (SQL_COPT_SS_BASE_EX+12) // Used to communicate with keystore providers
#define SQL_COPT_SS_AETRUSTEDCMKPATHS (SQL_COPT_SS_BASE_EX+13) // List of trusted CMK paths
#define SQL_COPT_SS_AECEKCACHETTL (SQL_COPT_SS_BASE_EX+14)// Symmetric Key Cache TTL
#define SQL_COPT_SS_EX_MAX_USED SQL_COPT_SS_AECEKCACHETTL
// SQLColAttributes driver specific defines.
// SQLSetDescField/SQLGetDescField driver specific defines.
@ -287,7 +300,10 @@ extern "C" {
// Legacy datetime related metadata
#define SQL_CA_SS_SERVER_TYPE (SQL_CA_SS_BASE+35) // column type to send on the wire for datetime types
#define SQL_CA_SS_MAX_USED (SQL_CA_SS_BASE+36)
// force column encryption
#define SQL_CA_SS_FORCE_ENCRYPT (SQL_CA_SS_BASE+36) // indicate mandatory encryption for this parameter
#define SQL_CA_SS_MAX_USED (SQL_CA_SS_BASE+37)
// Defines returned by SQL_ATTR_CURSOR_TYPE/SQL_CURSOR_TYPE
#define SQL_CURSOR_FAST_FORWARD_ONLY 8 // Only returned by SQLGetStmtAttr/Option
@ -299,6 +315,8 @@ extern "C" {
// Defines for use with SQL_COPT_SS_INTEGRATED_SECURITY - Pre-Connect Option only
#define SQL_IS_OFF 0L // Integrated security isn't used
#define SQL_IS_ON 1L // Integrated security is used
#define SQL_IS_AD_OFF 2L // Active Directory integrated security isn't used
#define SQL_IS_AD_ON 3L // Active Directory integrated security is used
#define SQL_IS_DEFAULT SQL_IS_OFF
// Defines for use with SQL_COPT_SS_PRESERVE_CURSORS
#define SQL_PC_OFF 0L // Cursors are closed on SQLTransact
@ -353,6 +371,16 @@ extern "C" {
#define SQL_CO_FFO_AF (SQL_CO_FFO|SQL_CO_AF) // Fast-forward cursor with autofetch
#define SQL_CO_FIREHOSE_AF 4L // Auto fetch on fire-hose cursors
#define SQL_CO_DEFAULT SQL_CO_OFF
// Defines for use with SQL_SOPT_SS_COLUMN_ENCRYPTION
#define SQL_CE_DISABLED 0L // Disabled
#define SQL_CE_RESULTSETONLY 1L // Decryption Only (resultsets and return values)
#define SQL_CE_ENABLED 3L // Enabled (both encryption and decryption)
// Defines for use with SQL_COPT_SS_COLUMN_ENCRYPTION
#define SQL_COLUMN_ENCRYPTION_DISABLE 0L
#define SQL_COLUMN_ENCRYPTION_ENABLE 1L
#define SQL_COLUMN_ENCRYPTION_DEFAULT SQL_COLUMN_ENCRYPTION_DISABLE
// Defines for use with SQL_COPT_SS_AECEKCACHETTL
#define SQL_AECEKCACHETTL_DEFAULT 7200L // TTL value in seconds (2 hours)
//SQL_SOPT_SS_NOCOUNT_STATUS
#define SQL_NC_OFF 0L
#define SQL_NC_ON 1L
@ -927,6 +955,60 @@ WCHAR* SQL_API bcp_gettypenameW (INT, DBBOOL);
#define SQL_AO_DEFAULT SQL_AO_OFF
#define SQL_CA_SS_BASE_COLUMN_NAME SQL_DESC_BASE_COLUMN_NAME
// Keystore Provider interface definition
typedef void errFunc(void *ctx, const wchar_t *msg, ...);
#define IDS_MSG(x) ((const wchar_t*)(x))
typedef struct AEKeystoreProvider
{
wchar_t *Name;
int (*Init)(void *ctx, errFunc *onError);
int (*Read)(void *ctx, errFunc *onError, void *data, unsigned int *len);
int (*Write)(void *ctx, errFunc *onError, void *data, unsigned int len);
int (*DecryptCEK)(
void *ctx,
errFunc *onError,
const wchar_t *keyPath,
const wchar_t *alg,
unsigned char *ecek,
unsigned short ecek_len,
unsigned char **cek_out,
unsigned short *cek_len);
void (*Free)();
} AEKEYSTOREPROVIDER;
/* Data is defined to be past the end of the structure header.
This is accepted by MSVC, GCC, and C99 standard but former emits
unnecessary warning, hence it has to be disabled.
*/
#pragma warning(push)
#pragma warning(disable:4200)
typedef struct AEKeystoreData
{
wchar_t *Name;
unsigned int dataSize;
char Data[];
} AEKEYSTOREPROVIDERDATA;
#pragma warning(pop)
// The following constants are for the Azure Key Vault configuration interface
#define AKV_CONFIG_FLAGS 0
#define AKVCFG_USECLIENTID 0x00000001
#define AKVCFG_AUTORENEW 0x00000002
#define AKV_CONFIG_CLIENTID 1
#define AKV_CONFIG_CLIENTKEY 2
#define AKV_CONFIG_ACCESSTOKEN 3
#define AKV_CONFIG_TOKENEXPIRY 4
#define AKV_CONFIG_MAXRETRIES 5
#define AKV_CONFIG_RETRYTIMEOUT 6
#define AKV_CONFIG_RETRYWAIT 7
#ifdef __cplusplus
} // extern "C"
@ -973,10 +1055,13 @@ HANDLE __stdcall OpenSqlFilestream (
#define SQL_COPT_SS_CONNECT_RETRY_COUNT (SQL_COPT_SS_BASE+34) // Post connection attribute used to get ConnectRetryCount
#define SQL_COPT_SS_CONNECT_RETRY_INTERVAL (SQL_COPT_SS_BASE+35) // Post connection attribute used to get ConnectRetryInterval
#define SQL_COPT_SS_CLIENT_CERTIFICATE (SQL_COPT_SS_BASE+36) // Client certificate
#define SQL_COPT_SS_CLIENT_CERTIFICATE_FALLBACK (SQL_COPT_SS_BASE+37) // Client certificate fallback
#ifdef SQL_COPT_SS_MAX_USED
#undef SQL_COPT_SS_MAX_USED
#endif // SQL_COPT_SS_MAX_USED
#define SQL_COPT_SS_MAX_USED SQL_COPT_SS_CONNECT_RETRY_INTERVAL
#define SQL_COPT_SS_MAX_USED SQL_COPT_SS_CLIENT_CERTIFICATE_FALLBACK
#ifndef _SQLUSERINSTANCE_H_
@ -988,6 +1073,19 @@ HANDLE __stdcall OpenSqlFilestream (
extern "C" {
#endif
struct _CERT_CONTEXT;
typedef _CERT_CONTEXT CERT_CONTEXT;
typedef const CERT_CONTEXT *PCCERT_CONTEXT;
// type definition for client certificate fallback function
typedef DWORD(WINAPI *PFnClientCertificateFallback)(
__in BOOL fHash,
__in_z LPCWSTR pszCertificate,
__out PCCERT_CONTEXT *ppCertContext,
__out DWORD *pdwFlags,
__out ULONG cchKeyContainer,
__out_ecount(cchKeyContainer) WCHAR *pwchKeyContainer
);
// Recommended buffer size to store a LocalDB connection string
#define LOCALDB_MAX_SQLCONNECTION_BUFFER_SIZE 260

View file

@ -265,8 +265,8 @@ extern "C" {
ZEND_BEGIN_MODULE_GLOBALS(sqlsrv)
// global objects for errors and warnings. These are returned by sqlsrv_errors.
zval* errors;
zval* warnings;
zval errors;
zval warnings;
// flags for error handling and logging (set via sqlsrv_configure or php.ini)
zend_long log_severity;
@ -386,24 +386,24 @@ void __cdecl sqlsrv_error_dtor( zend_resource *rsrc TSRMLS_DC );
// release current error lists and set to NULL
inline void reset_errors( TSRMLS_D )
{
if( Z_TYPE_P( SQLSRV_G( errors )) != IS_ARRAY && Z_TYPE_P( SQLSRV_G( errors )) != IS_NULL ) {
if( Z_TYPE( SQLSRV_G( errors )) != IS_ARRAY && Z_TYPE( SQLSRV_G( errors )) != IS_NULL ) {
DIE( "sqlsrv_errors contains an invalid type" );
}
if( Z_TYPE_P( SQLSRV_G( warnings )) != IS_ARRAY && Z_TYPE_P( SQLSRV_G( warnings )) != IS_NULL ) {
if( Z_TYPE( SQLSRV_G( warnings )) != IS_ARRAY && Z_TYPE( SQLSRV_G( warnings )) != IS_NULL ) {
DIE( "sqlsrv_warnings contains an invalid type" );
}
if( Z_TYPE_P( SQLSRV_G( errors )) == IS_ARRAY ) {
zend_hash_destroy( Z_ARRVAL_P( SQLSRV_G( errors )));
FREE_HASHTABLE( Z_ARRVAL_P( SQLSRV_G( errors )));
if( Z_TYPE( SQLSRV_G( errors )) == IS_ARRAY ) {
zend_hash_destroy( Z_ARRVAL( SQLSRV_G( errors )));
FREE_HASHTABLE( Z_ARRVAL( SQLSRV_G( errors )));
}
if( Z_TYPE_P( SQLSRV_G( warnings )) == IS_ARRAY ) {
zend_hash_destroy( Z_ARRVAL_P( SQLSRV_G( warnings )));
FREE_HASHTABLE( Z_ARRVAL_P( SQLSRV_G( warnings )));
if( Z_TYPE( SQLSRV_G( warnings )) == IS_ARRAY ) {
zend_hash_destroy( Z_ARRVAL( SQLSRV_G( warnings )));
FREE_HASHTABLE( Z_ARRVAL( SQLSRV_G( warnings )));
}
ZVAL_NULL( SQLSRV_G( errors ));
ZVAL_NULL( SQLSRV_G( warnings ));
ZVAL_NULL( &SQLSRV_G( errors ));
ZVAL_NULL( &SQLSRV_G( warnings ));
}
#define THROW_SS_ERROR( ctx, error_code, ... ) \

View file

@ -88,8 +88,9 @@ const char SS_SQLSRV_WARNING_PARAM_VAR_NOT_REF[] = "Variable parameter %d not pa
/* internal functions */
zval* convert_to_zval( SQLSRV_PHPTYPE sqlsrv_php_type, void* in_val, SQLLEN field_len );
void fetch_fields_common( __inout ss_sqlsrv_stmt* stmt, zend_long fetch_type, __out zval* return_value, bool allow_empty_field_names
void convert_to_zval( sqlsrv_stmt* stmt, SQLSRV_PHPTYPE sqlsrv_php_type, void* in_val, SQLLEN field_len, zval& out_zval );
void fetch_fields_common( __inout ss_sqlsrv_stmt* stmt, zend_long fetch_type, __out zval& fields, bool allow_empty_field_names
TSRMLS_DC );
bool determine_column_size_or_precision( sqlsrv_stmt const* stmt, sqlsrv_sqltype sqlsrv_type, __out SQLULEN* column_size,
__out SQLSMALLINT* decimal_digits );
@ -405,8 +406,10 @@ PHP_FUNCTION( sqlsrv_fetch_array )
if( !result ) {
RETURN_NULL();
}
fetch_fields_common( stmt, fetch_type, return_value, true /*allow_empty_field_names*/ TSRMLS_CC );
zval fields;
ZVAL_UNDEF( &fields );
fetch_fields_common( stmt, fetch_type, fields, true /*allow_empty_field_names*/ TSRMLS_CC );
RETURN_ARR( Z_ARRVAL( fields ));
}
catch( core::CoreException& ) {
@ -768,6 +771,8 @@ PHP_FUNCTION( sqlsrv_fetch_object )
char* class_name = const_cast<char*>( STDCLASS_NAME );
std::size_t class_name_len = STDCLASS_NAME_LEN;
HashTable* properties_ht = NULL;
zval retval_z;
ZVAL_UNDEF( &retval_z );
// retrieve the statement resource and optional fetch type (see enum SQLSRV_FETCH_TYPE),
// fetch style (see SQLSRV_SCROLL_* constants) and fetch offset
@ -802,8 +807,8 @@ PHP_FUNCTION( sqlsrv_fetch_object )
RETURN_NULL();
}
fetch_fields_common( stmt, SQLSRV_FETCH_ASSOC, return_value, false /*allow_empty_field_names*/ TSRMLS_CC );
properties_ht = Z_ARRVAL_P( return_value );
fetch_fields_common( stmt, SQLSRV_FETCH_ASSOC, retval_z, false /*allow_empty_field_names*/ TSRMLS_CC );
properties_ht = Z_ARRVAL( retval_z );
// find the zend_class_entry of the class the user requested (stdClass by default) for use below
zend_class_entry* class_entry = NULL;
@ -815,7 +820,7 @@ PHP_FUNCTION( sqlsrv_fetch_object )
// create an instance of the object with its default properties
// we pass NULL for the properties so that the object will be populated by its default properties
zr = object_and_properties_init( return_value, class_entry, NULL /*properties*/ );
zr = object_and_properties_init( &retval_z, class_entry, NULL /*properties*/ );
CHECK_ZEND_ERROR( zr, stmt, SS_SQLSRV_ERROR_ZEND_OBJECT_FAILED, class_name ) {
throw ss::SSException();
}
@ -825,7 +830,7 @@ PHP_FUNCTION( sqlsrv_fetch_object )
// causes duplicate properties when the visibilities are different and also references the
// default parameters directly in the object, meaning the default property value is changed when
// the object's property is changed.
zend_merge_properties( return_value, properties_ht TSRMLS_CC );
zend_merge_properties( &retval_z, properties_ht TSRMLS_CC );
// find and call the object's constructor
@ -853,16 +858,16 @@ PHP_FUNCTION( sqlsrv_fetch_object )
num_params = zend_hash_num_elements( ctor_params_ht );
params_m = reinterpret_cast<zval*>( sqlsrv_malloc( num_params * sizeof( zval ) ));
int i;
for( i = 0, zend_hash_internal_pointer_reset( ctor_params_ht );
zend_hash_has_more_elements( ctor_params_ht ) == SUCCESS;
zend_hash_move_forward( ctor_params_ht ), ++i ) {
ZVAL_COPY_VALUE( &params_m[i], zend_hash_get_current_data_ex(ctor_params_ht, &ctor_params_ht->nInternalPointer ) );
int i = 0;
zval* value_z = NULL;
ZEND_HASH_FOREACH_VAL( ctor_params_ht, value_z ) {
ZVAL_COPY_VALUE( &params_m[i], value_z );
zr = ( NULL != &params_m[i] ) ? SUCCESS : FAILURE;
CHECK_ZEND_ERROR( zr, stmt, SS_SQLSRV_ERROR_ZEND_OBJECT_FAILED, class_name ) {
throw ss::SSException();
}
} //for
i++;
} ZEND_HASH_FOREACH_END();
} //if( !Z_ISUNDEF( ctor_params_z ))
// call the constructor function itself.
@ -877,14 +882,14 @@ PHP_FUNCTION( sqlsrv_fetch_object )
fci.param_count = num_params;
fci.params = params_m; // purposefully not transferred since ownership isn't actually transferred.
fci.object = reinterpret_cast<zend_object*>(return_value);
fci.object = reinterpret_cast<zend_object*>( &retval_z );
memset( &fcic, 0, sizeof( fcic ));
fcic.initialized = 1;
fcic.function_handler = class_entry->constructor;
fcic.calling_scope = class_entry;
fci.object = reinterpret_cast<zend_object*>(return_value);
fci.object = reinterpret_cast<zend_object*>( &retval_z );
zr = zend_call_function( &fci, &fcic TSRMLS_CC );
CHECK_ZEND_ERROR( zr, stmt, SS_SQLSRV_ERROR_ZEND_OBJECT_FAILED, class_name ) {
@ -892,6 +897,7 @@ PHP_FUNCTION( sqlsrv_fetch_object )
}
} //if( class_entry->constructor )
RETURN_ZVAL( &retval_z, 1, 1 );
}
catch( core::CoreException& ) {
@ -1055,6 +1061,8 @@ PHP_FUNCTION( sqlsrv_get_field )
void* field_value = NULL;
int field_index = -1;
SQLLEN field_len = -1;
zval retval_z;
ZVAL_UNDEF(&retval_z);
// get the statement, the field index and the optional type
PROCESS_PARAMS( stmt, "rl|l", _FN_, 2, &field_index, &sqlsrv_php_type );
@ -1068,11 +1076,11 @@ PHP_FUNCTION( sqlsrv_get_field )
THROW_SS_ERROR( stmt, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ );
}
core_sqlsrv_get_field( stmt, field_index, sqlsrv_php_type, false, &field_value, &field_len, false/*cache_field*/,
core_sqlsrv_get_field( stmt, field_index, sqlsrv_php_type, false, field_value, &field_len, false/*cache_field*/,
&sqlsrv_php_type_out TSRMLS_CC );
zval* retval_z = convert_to_zval(sqlsrv_php_type_out, field_value, field_len);
RETURN_ZVAL(retval_z, 1, 1);
convert_to_zval( stmt, sqlsrv_php_type_out, field_value, field_len, retval_z );
sqlsrv_free( field_value );
RETURN_ZVAL( &retval_z, 1, 1 );
}
catch( core::CoreException& ) {
@ -1166,23 +1174,19 @@ void mark_params_by_reference( ss_sqlsrv_stmt* stmt, zval* params_z TSRMLS_DC )
HashTable* params_ht = Z_ARRVAL_P( params_z );
for( zend_hash_internal_pointer_reset( params_ht );
zend_hash_has_more_elements( params_ht ) == SUCCESS;
zend_hash_move_forward( params_ht )) {
zend_ulong index;
zend_string* key = NULL;
zend_ulong index = -1;
zval* value_z = NULL;
ZEND_HASH_FOREACH_KEY_VAL( params_ht, index, key, value_z ) {
// make sure it's an integer index
int type = zend_hash_get_current_key( params_ht, &key, &index);
int type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG;
CHECK_CUSTOM_ERROR( type != HASH_KEY_IS_LONG, stmt, SS_SQLSRV_ERROR_PARAM_INVALID_INDEX ) {
throw ss::SSException();
}
core::sqlsrv_zend_hash_get_current_data( *stmt, params_ht, value_z TSRMLS_CC );
// This code turns parameters into references. Since the function declaration cannot
// pass array elements as references (without requiring & in front of each variable),
// we have to set the reference in each of the zvals ourselves. In the event of a
@ -1201,7 +1205,7 @@ void mark_params_by_reference( ss_sqlsrv_stmt* stmt, zval* params_z TSRMLS_DC )
}
ZVAL_MAKE_REF( var );
}
}
} ZEND_HASH_FOREACH_END();
// save our parameters for later.
Z_TRY_ADDREF_P( params_z );
@ -1225,13 +1229,11 @@ void bind_params( ss_sqlsrv_stmt* stmt TSRMLS_DC )
HashTable* params_ht = Z_ARRVAL_P( params_z );
for( zend_hash_internal_pointer_reset( params_ht );
zend_hash_has_more_elements( params_ht ) == SUCCESS;
zend_hash_move_forward( params_ht )) {
zend_string *key = NULL;
zend_ulong index = -1;
zend_string *key = NULL;
zval* param_z = NULL;
ZEND_HASH_FOREACH_KEY_VAL( params_ht, index, key, param_z ) {
zval* value_z = NULL;
SQLSMALLINT direction = SQL_PARAM_INPUT;
SQLSRV_ENCODING encoding = stmt->encoding();
@ -1244,14 +1246,11 @@ void bind_params( ss_sqlsrv_stmt* stmt TSRMLS_DC )
SQLSRV_PHPTYPE php_out_type = SQLSRV_PHPTYPE_INVALID;
// make sure it's an integer index
int type = zend_hash_get_current_key( params_ht, &key, &index);
int type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG;
CHECK_CUSTOM_ERROR( type != HASH_KEY_IS_LONG, stmt, SS_SQLSRV_ERROR_PARAM_INVALID_INDEX ) {
throw ss::SSException();
}
core::sqlsrv_zend_hash_get_current_data( *stmt, params_ht, param_z TSRMLS_CC );
// if it's a parameter array
if( Z_TYPE_P( param_z ) == IS_ARRAY ) {
@ -1273,7 +1272,7 @@ void bind_params( ss_sqlsrv_stmt* stmt TSRMLS_DC )
core_sqlsrv_bind_param( stmt, index, direction, value_z, php_out_type, encoding, sql_type, column_size,
decimal_digits TSRMLS_CC );
}
} ZEND_HASH_FOREACH_END();
}
catch( core::CoreException& ) {
SQLFreeStmt( stmt->handle(), SQL_RESET_PARAMS );
@ -1482,14 +1481,11 @@ void stmt_option_scrollable:: operator()( sqlsrv_stmt* stmt, stmt_option const*
namespace {
zval* convert_to_zval( SQLSRV_PHPTYPE sqlsrv_php_type, void* in_val, SQLLEN field_len )
void convert_to_zval(sqlsrv_stmt* stmt, SQLSRV_PHPTYPE sqlsrv_php_type, void* in_val, SQLLEN field_len, zval& out_zval)
{
zval* out_zval = NULL;
if ( in_val == NULL ) {
out_zval = ( zval* )sqlsrv_malloc(sizeof( zval ));
ZVAL_NULL( out_zval );
return out_zval;
ZVAL_NULL( &out_zval);
return;
}
switch (sqlsrv_php_type) {
@ -1497,53 +1493,47 @@ zval* convert_to_zval( SQLSRV_PHPTYPE sqlsrv_php_type, void* in_val, SQLLEN fiel
case SQLSRV_PHPTYPE_INT:
case SQLSRV_PHPTYPE_FLOAT:
{
out_zval = ( zval* )sqlsrv_malloc(sizeof( zval ));
ZVAL_UNDEF( out_zval );
if (sqlsrv_php_type == SQLSRV_PHPTYPE_INT) {
ZVAL_LONG( out_zval, *( static_cast<int*>( in_val )));
ZVAL_LONG( &out_zval, *(static_cast<int*>( in_val )));
}
else {
ZVAL_DOUBLE( out_zval, *( static_cast<double*>( in_val )));
ZVAL_DOUBLE( &out_zval, *(static_cast<double*>( in_val )));
}
sqlsrv_free( in_val );
break;
}
case SQLSRV_PHPTYPE_STRING:
{
out_zval = ( zval* )sqlsrv_malloc( sizeof( zval ));
ZVAL_UNDEF( out_zval );
core::sqlsrv_zval_stringl( out_zval, reinterpret_cast<char*>( in_val ), field_len );
sqlsrv_free( in_val );
ZVAL_STRINGL( &out_zval, static_cast<const char*>( in_val ), field_len);
break;
}
case SQLSRV_PHPTYPE_STREAM:
{
out_zval = *( static_cast<zval*>( in_val ));
stmt->active_stream = out_zval;
//addref here because deleting out_zval later will decrement the refcount
Z_TRY_ADDREF( out_zval );
break;
}
case SQLSRV_PHPTYPE_DATETIME:
{
out_zval = ( static_cast<zval*>( in_val ) );
//addref here because deleting out_zval later will decrement the refcount
Z_TRY_ADDREF_P( out_zval );
in_val = NULL;
out_zval = *( static_cast<zval*>( in_val ));
break;
}
case SQLSRV_PHPTYPE_NULL:
out_zval = ( zval* )sqlsrv_malloc(sizeof( zval ));
ZVAL_NULL( out_zval );
ZVAL_NULL(&out_zval);
break;
default:
DIE("Unknown php type");
break;
}
return out_zval;
return;
}
// put in the column size and scale/decimal digits of the sql server type
// these values are taken from the MSDN page at http://msdn2.microsoft.com/en-us/library/ms711786(VS.85).aspx
bool determine_column_size_or_precision( sqlsrv_stmt const* stmt, sqlsrv_sqltype sqlsrv_type, __out SQLULEN* column_size,
@ -1795,15 +1785,13 @@ void determine_stmt_has_rows( ss_sqlsrv_stmt* stmt TSRMLS_DC )
}
}
void fetch_fields_common( __inout ss_sqlsrv_stmt* stmt, zend_long fetch_type, __out zval* return_value, bool allow_empty_field_names
void fetch_fields_common( __inout ss_sqlsrv_stmt* stmt, zend_long fetch_type, __out zval& fields, bool allow_empty_field_names
TSRMLS_DC )
{
void* field_value = NULL;
sqlsrv_phptype sqlsrv_php_type;
sqlsrv_php_type.typeinfo.type = SQLSRV_PHPTYPE_INVALID;
SQLSRV_PHPTYPE sqlsrv_php_type_out = SQLSRV_PHPTYPE_INVALID;
zval fields;
ZVAL_UNDEF( &fields );
// make sure that the fetch type is legal
CHECK_CUSTOM_ERROR((fetch_type < MIN_SQLSRV_FETCH || fetch_type > MAX_SQLSRV_FETCH), stmt, SS_SQLSRV_ERROR_INVALID_FETCH_TYPE, stmt->func()) {
@ -1842,24 +1830,22 @@ void fetch_fields_common( __inout ss_sqlsrv_stmt* stmt, zend_long fetch_type, __
throw ss::SSException();
}
// get the fields
for( int i = 0; i < num_cols; ++i ) {
zval field;
SQLLEN field_len = -1;
core_sqlsrv_get_field( stmt, i, sqlsrv_php_type, true /*prefer string*/,
&field_value, &field_len, false /*cache_field*/, &sqlsrv_php_type_out TSRMLS_CC );
field = *(convert_to_zval( sqlsrv_php_type_out, field_value, field_len ));
field_value, &field_len, false /*cache_field*/, &sqlsrv_php_type_out TSRMLS_CC );
zval field;
ZVAL_UNDEF( &field );
convert_to_zval( stmt, sqlsrv_php_type_out, field_value, field_len, field );
sqlsrv_free( field_value );
if( fetch_type & SQLSRV_FETCH_NUMERIC ) {
zr = add_next_index_zval( &fields, &field );
CHECK_ZEND_ERROR( zr, stmt, SQLSRV_ERROR_ZEND_HASH ) {
throw ss::SSException();
}
Z_TRY_ADDREF_P(&field);
}
if( fetch_type & SQLSRV_FETCH_ASSOC ) {
@ -1875,14 +1861,16 @@ void fetch_fields_common( __inout ss_sqlsrv_stmt* stmt, zend_long fetch_type, __
CHECK_ZEND_ERROR( zr, stmt, SQLSRV_ERROR_ZEND_HASH ) {
throw ss::SSException();
}
Z_TRY_ADDREF_P(&field);
}
}
//only addref when the fetch_type is BOTH because this is the only case when fields(hashtable)
//has 2 elements pointing to field. Do not addref if the type is NUMBERIC or ASSOC because
//fields now only has 1 element pointing to field and we want the ref count to be only 1
if (fetch_type == SQLSRV_FETCH_BOTH) {
Z_TRY_ADDREF(field);
}
} //for loop
*return_value = fields;
ZVAL_NULL( &fields );
zval_ptr_dtor( &fields );
}
void parse_param_array( ss_sqlsrv_stmt* stmt, __inout zval* param_array, zend_ulong index, __out SQLSMALLINT& direction,
@ -2123,21 +2111,20 @@ bool is_valid_sqlsrv_sqltype( sqlsrv_sqltype sql_type )
// of standard encodings created at module initialization time
bool verify_and_set_encoding( const char* encoding_string, __out sqlsrv_phptype& phptype_encoding TSRMLS_DC )
{
for( zend_hash_internal_pointer_reset( g_ss_encodings_ht );
zend_hash_has_more_elements( g_ss_encodings_ht ) == SUCCESS;
zend_hash_move_forward( g_ss_encodings_ht ) ) {
sqlsrv_encoding* encoding;
int zr = (encoding = reinterpret_cast<sqlsrv_encoding*>(zend_hash_get_current_data_ptr(g_ss_encodings_ht))) != NULL ? SUCCESS : FAILURE;
if( zr == FAILURE ) {
void* encoding_temp = NULL;
zend_ulong index = -1;
zend_string* key = NULL;
ZEND_HASH_FOREACH_KEY_PTR( g_ss_encodings_ht, index, key, encoding_temp ) {
if ( !encoding_temp ) {
DIE( "Fatal: Error retrieving encoding from encoding hash table." );
}
sqlsrv_encoding* encoding = reinterpret_cast<sqlsrv_encoding*>( encoding_temp );
encoding_temp = NULL;
if( !stricmp( encoding_string, encoding->iana )) {
phptype_encoding.typeinfo.encoding = encoding->code_page;
return true;
}
}
} ZEND_HASH_FOREACH_END();
return false;
}

View file

@ -61,7 +61,7 @@ BEGIN
BEGIN
VALUE "Comments", "This product includes PHP software that is freely available from http://www.php.net/software/. Copyright © 2001-2016 The PHP Group. All rights reserved.\0"
VALUE "CompanyName", "Microsoft Corp.\0"
VALUE "FileDescription", "Microsoft Drivers for PHP for SQL Server (SQLSRV Driver)\0"
VALUE "FileDescription", "Microsoft Drivers for PHP for SQL Server (PDO Driver)\0"
VALUE "FileVersion", STRVER4(SQLVERSION_MAJOR,SQLVERSION_MINOR, SQLVERSION_MMDD, SQLVERSION_REVISION)
VALUE "InternalName", FILE_NAME "\0"
VALUE "LegalCopyright", "Copyright Microsoft Corporation.\0"

View file

@ -415,7 +415,7 @@ bool ss_error_handler(sqlsrv_context& ctx, unsigned int sqlsrv_error_code, bool
severity = SEV_WARNING;
}
return handle_errors_and_warnings( ctx, SQLSRV_G( errors ), SQLSRV_G( warnings ), severity, sqlsrv_error_code, warning,
return handle_errors_and_warnings( ctx, &SQLSRV_G( errors ), &SQLSRV_G( warnings ), severity, sqlsrv_error_code, warning,
print_args TSRMLS_CC );
}
@ -464,49 +464,35 @@ PHP_FUNCTION( sqlsrv_errors )
LOG_FUNCTION( "sqlsrv_errors" );
if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flags ) == FAILURE ) {
if(( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flags ) == FAILURE ) ||
( flags != SQLSRV_ERR_ALL && flags != SQLSRV_ERR_ERRORS && flags != SQLSRV_ERR_WARNINGS )) {
LOG( SEV_ERROR, "An invalid parameter was passed to %1!s!.", _FN_ );
RETURN_FALSE;
}
if( flags == SQLSRV_ERR_ALL ) {
int result;
zval both_z;
ZVAL_UNDEF( &both_z );
result = array_init( &both_z );
zval err_z;
ZVAL_UNDEF( &err_z );
result = array_init( &err_z );
if( result == FAILURE ) {
RETURN_FALSE;
}
if( flags == SQLSRV_ERR_ALL || flags == SQLSRV_ERR_ERRORS ) {
if( Z_TYPE( SQLSRV_G( errors )) == IS_ARRAY && !sqlsrv_merge_zend_hash( &err_z, &SQLSRV_G( errors ) TSRMLS_CC )) {
RETURN_FALSE;
}
if( Z_TYPE_P( SQLSRV_G( errors )) == IS_ARRAY && !sqlsrv_merge_zend_hash( &both_z, SQLSRV_G( errors ) TSRMLS_CC )) {
}
if( flags == SQLSRV_ERR_ALL || flags == SQLSRV_ERR_WARNINGS ) {
if( Z_TYPE( SQLSRV_G( warnings )) == IS_ARRAY && !sqlsrv_merge_zend_hash( &err_z, &SQLSRV_G( warnings ) TSRMLS_CC )) {
RETURN_FALSE;
}
if( Z_TYPE_P( SQLSRV_G( warnings )) == IS_ARRAY && !sqlsrv_merge_zend_hash( &both_z, SQLSRV_G( warnings ) TSRMLS_CC )) {
RETURN_FALSE;
}
if( zend_hash_num_elements( Z_ARRVAL_P( &both_z )) == 0 ) {
if( zend_hash_num_elements( Z_ARRVAL_P( &err_z )) == 0 ) {
RETURN_NULL();
}
RETURN_ZVAL( &both_z , 1, 1 );
}
else if( flags == SQLSRV_ERR_WARNINGS ) {
RETURN_ZVAL( SQLSRV_G( warnings ), 1, 0 );
}
else {
RETURN_ZVAL( SQLSRV_G( errors ), 1, 0 );
}
RETURN_ZVAL( &err_z, 1, 1 );
}
// sqlsrv_configure( string $setting, mixed $value )
@ -875,11 +861,12 @@ bool handle_errors_and_warnings( sqlsrv_context& ctx, zval* reported_chain, zval
// see RINIT in init.cpp for information about which errors are ignored.
bool ignore_warning( char* sql_state, int native_code TSRMLS_DC )
{
for( zend_hash_internal_pointer_reset( g_ss_warnings_to_ignore_ht );
zend_hash_has_more_elements( g_ss_warnings_to_ignore_ht ) == SUCCESS;
zend_hash_move_forward( g_ss_warnings_to_ignore_ht ) ) {
zend_ulong index = -1;
zend_string* key = NULL;
void* error_temp = NULL;
sqlsrv_error* error = static_cast<sqlsrv_error*>(zend_hash_get_current_data_ptr( g_ss_warnings_to_ignore_ht ));
ZEND_HASH_FOREACH_KEY_PTR( g_ss_warnings_to_ignore_ht, index, key, error_temp ) {
sqlsrv_error* error = static_cast<sqlsrv_error*>( error_temp );
if (NULL == error) {
return false;
}
@ -888,7 +875,7 @@ bool ignore_warning( char* sql_state, int native_code TSRMLS_DC )
( error->native_code == native_code || error->native_code == -1 )) {
return true;
}
}
} ZEND_HASH_FOREACH_END();
return false;
}
@ -911,27 +898,24 @@ bool sqlsrv_merge_zend_hash( __inout zval* dest_z, zval const* src_z TSRMLS_DC )
}
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 ) ) {
zend_ulong index = -1;
zend_string* key = NULL;
zval* value_z = NULL;
result = (value_z = zend_hash_get_current_data( src_ht )) != NULL ? SUCCESS : FAILURE;
if( result == FAILURE ) {
ZEND_HASH_FOREACH_KEY_VAL( src_ht, index, key, value_z ) {
if ( !value_z ) {
zend_hash_apply( Z_ARRVAL_P(dest_z), sqlsrv_merge_zend_hash_dtor TSRMLS_CC );
return false;
}
result = add_next_index_zval( dest_z, value_z );
int 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;
}
Z_TRY_ADDREF_P( value_z );
}
} ZEND_HASH_FOREACH_END();
return true;
}