diff --git a/pdo_sqlsrv/core_conn.cpp b/pdo_sqlsrv/core_conn.cpp index 825393e7..2c8f423a 100644 --- a/pdo_sqlsrv/core_conn.cpp +++ b/pdo_sqlsrv/core_conn.cpp @@ -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,39 +124,49 @@ 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 ); - build_connection_string_and_set_conn_attr( conn, server, uid, pwd, options_ht, valid_conn_opts, driver, - conn_str TSRMLS_CC ); - - // We only support UTF-8 encoding for connection string. - // Convert our UTF-8 connection string to UTF-16 before connecting with SQLDriverConnnectW - wconn_len = static_cast( conn_str.length() + 1 ) * sizeof( wchar_t ); + // We only support UTF-8 encoding for connection string. + // Convert our UTF-8 connection string to UTF-16 before connecting with SQLDriverConnnectW + wconn_len = static_cast( conn_str.length() + 1 ) * sizeof( wchar_t ); - wconn_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_UTF8, conn_str.c_str(), static_cast( conn_str.length() ), &wconn_len ); - CHECK_CUSTOM_ERROR( wconn_string == NULL, conn, SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE, get_last_error_message() ) - { - throw core::CoreException(); - } - - SQLSMALLINT output_conn_size; - r = SQLDriverConnectW( conn->handle(), NULL, reinterpret_cast( wconn_string.get() ), - static_cast( wconn_len ), NULL, - 0, &output_conn_size, SQL_DRIVER_NOPROMPT ); - // clear the connection string from memory to remove sensitive data (such as a password). - memset( const_cast( conn_str.c_str()), 0, conn_str.size() ); - memset( wconn_string, 0, wconn_len * sizeof( wchar_t )); // wconn_len is the number of characters, not bytes - conn_str.clear(); + wconn_string = utf16_string_from_mbcs_string(SQLSRV_ENCODING_UTF8, conn_str.c_str(), static_cast(conn_str.length()), &wconn_len); + CHECK_CUSTOM_ERROR( wconn_string == NULL, conn, SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE, get_last_error_message()) + { + throw core::CoreException(); + } - if( !SQL_SUCCEEDED( r )) { - SQLCHAR state[ SQL_SQLSTATE_BUFSIZE ]; - SQLSMALLINT len; - SQLRETURN r = SQLGetDiagField( SQL_HANDLE_DBC, conn->handle(), 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len ); - // 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() ) { - throw core::CoreException(); - } - } + SQLSMALLINT output_conn_size; + r = SQLDriverConnectW( conn->handle(), NULL, reinterpret_cast( wconn_string.get()), + static_cast( wconn_len ), NULL, + 0, &output_conn_size, SQL_DRIVER_NOPROMPT ); + // clear the connection string from memory to remove sensitive data (such as a password). + memset( const_cast( conn_str.c_str()), 0, conn_str.size() ); + memset( wconn_string, 0, wconn_len * sizeof( wchar_t )); // wconn_len is the number of characters, not bytes + conn_str.clear(); + + if( !SQL_SUCCEEDED( r )) { + 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( 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( i ); + break; + } + + } CHECK_SQL_ERROR( r, conn ) { throw core::CoreException(); } @@ -168,7 +178,7 @@ sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_ // determine the version of the server we're connected to. The server version is left in the // connection upon return. determine_server_version( conn TSRMLS_CC ); - + } catch( std::bad_alloc& ) { memset( const_cast( conn_str.c_str()), 0, conn_str.size() ); @@ -532,9 +542,7 @@ void build_connection_string_and_set_conn_attr( sqlsrv_conn* conn, const char* s int zr = SUCCESS; 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,30 +594,25 @@ 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; - zval* data = NULL; + zend_string *key = NULL; + zend_ulong index = -1; + zval* data = NULL; - type = zend_hash_get_current_key( options, &key, &index ); - - // 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 ); + 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; - conn_opt = get_connection_option( conn, index, valid_conn_opts TSRMLS_CC ); - - if( index == SQLSRV_CONN_OPTION_MARS ) { - mars_mentioned = true; - } - - conn_opt->func( conn_opt, data, conn, connection_string TSRMLS_CC ); - } + // 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." ); + + conn_opt = get_connection_option( conn, index, valid_conn_opts TSRMLS_CC ); + + if( index == SQLSRV_CONN_OPTION_MARS ) { + mars_mentioned = true; + } + + 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"; diff --git a/pdo_sqlsrv/core_sqlsrv.h b/pdo_sqlsrv/core_sqlsrv.h index 2563ee70..840cea7f 100644 --- a/pdo_sqlsrv/core_sqlsrv.h +++ b/pdo_sqlsrv/core_sqlsrv.h @@ -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,8 +1323,8 @@ 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 SQLSRV_PHPTYPE *sqlsrv_php_type_out TSRMLS_DC ); + __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 ); void core_sqlsrv_post_param( sqlsrv_stmt* stmt, zend_ulong paramno, zval* param_z TSRMLS_DC ); @@ -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( 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( err_msg ), CONNECTION_BUSY_ODBC_ERROR[driver_version] )) { THROW_CORE_ERROR( stmt, SQLSRV_ERROR_MARS_OFF ); } diff --git a/pdo_sqlsrv/core_stmt.cpp b/pdo_sqlsrv/core_stmt.cpp index e727262f..4937b5b3 100644 --- a/pdo_sqlsrv/core_stmt.cpp +++ b/pdo_sqlsrv/core_stmt.cpp @@ -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, @@ -91,8 +91,8 @@ void default_sql_type( sqlsrv_stmt* stmt, SQLULEN paramno, zval* param_z, SQLSRV __out SQLSMALLINT& sql_type TSRMLS_DC ); 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 ); +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 ); 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, ¶m_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,32 +248,26 @@ 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_ulong index = -1; + zend_string *key = NULL; + zval* value_z = NULL; - zend_string *key = NULL; - zend_ulong index = -1; - zval* value_z = NULL; + ZEND_HASH_FOREACH_KEY_VAL( options_ht, index, key, value_z ) { - int type = zend_hash_get_current_key( options_ht, &key, &index); - - // 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. - // The driver layer should ensure that the key is valid. - DEBUG_SQLSRV_ASSERT( stmt_opt != NULL, "allocate_stmt: unexpected null value for statement option." ); - - // perform the actions the statement option needs done. - (*stmt_opt->func)( stmt, stmt_opt, value_z TSRMLS_CC ); - } + int type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG; - zend_hash_internal_pointer_end( options_ht ); + // The driver layer should ensure a valid key. + DEBUG_SQLSRV_ASSERT(( type == HASH_KEY_IS_LONG ), "allocate_stmt: Invalid statment option key provided." ); + + 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. + // The driver layer should ensure that the key is valid. + DEBUG_SQLSRV_ASSERT( stmt_opt != NULL, "allocate_stmt: unexpected null value for statement option." ); + + // perform the actions the statement option needs done. + (*stmt_opt->func)( stmt, stmt_opt, value_z TSRMLS_CC ); + } 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 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 - zend_hash_clean( Z_ARRVAL( stmt->param_streams )); - + if ( stmt->send_streams_at_exec ) { + zend_hash_clean( Z_ARRVAL( stmt->param_streams )); + } } catch( core::CoreException& e ) { @@ -869,100 +864,100 @@ 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 SQLSRV_PHPTYPE *sqlsrv_php_type_out TSRMLS_DC ) + __out void*& field_value, __out SQLLEN* field_len, bool cache_field, + __out SQLSRV_PHPTYPE *sqlsrv_php_type_out TSRMLS_DC) { - try { - - // close the stream to release the resource - close_active_stream( stmt TSRMLS_CC ); - - // if the field has been retrieved before, return the previous result - field_cache* cached = NULL; - if (NULL != (cached = reinterpret_cast(zend_hash_index_find_ptr(Z_ARRVAL(stmt->field_cache), static_cast(field_index))))) { - // the field value is NULL - if( cached->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 ); - if( cached->type.typeinfo.type == SQLSRV_PHPTYPE_STRING ) { - // prevent the 'string not null terminated' warning - reinterpret_cast( *field_value )[ cached->len ] = '\0'; - } - *field_len = cached->len; - if( sqlsrv_php_type_out ) { *sqlsrv_php_type_out = static_cast( cached->type.typeinfo.type ); } - } - return; - } - - sqlsrv_phptype sqlsrv_php_type = sqlsrv_php_type_in; + try { - SQLLEN sql_field_type = 0; - SQLLEN sql_field_len = 0; + // close the stream to release the resource + close_active_stream(stmt TSRMLS_CC); - // Make sure that the statement was executed and not just prepared. - CHECK_CUSTOM_ERROR( !stmt->executed, stmt, SQLSRV_ERROR_STATEMENT_NOT_EXECUTED ) { - throw core::CoreException(); - } + // if the field has been retrieved before, return the previous result + field_cache* cached = NULL; + if (NULL != ( cached = static_cast( zend_hash_index_find_ptr( Z_ARRVAL( stmt->field_cache ), static_cast( field_index ))))) { + // the field value is NULL + if( cached->value == NULL ) { + field_value = NULL; + *field_len = 0; + if( sqlsrv_php_type_out ) { *sqlsrv_php_type_out = SQLSRV_PHPTYPE_NULL; } + } + else { - // if the field is to be cached, and this field is being retrieved out of order, cache prior fields so they - // may also be retrieved. - if( cache_field && ( field_index - stmt->last_field_index ) >= 2 ) { - sqlsrv_phptype invalid; - invalid.typeinfo.type = SQLSRV_PHPTYPE_INVALID; - for( int i = stmt->last_field_index + 1; i < field_index; ++i ) { - SQLSRV_ASSERT((cached = reinterpret_cast(zend_hash_index_find_ptr(Z_ARRVAL(stmt->field_cache), i))) == NULL, - "Field already cached." ); - 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; - *field_len = 0; - } - } - } + 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( field_value )[ cached->len ] = '\0'; + } + *field_len = cached->len; + if( sqlsrv_php_type_out) { *sqlsrv_php_type_out = static_cast(cached->type.typeinfo.type); } + } + return; + } - // If the php type was not specified set the php type to be the default type. - if( sqlsrv_php_type.typeinfo.type == SQLSRV_PHPTYPE_INVALID ) { - - // Get the SQL type of the field. - core::SQLColAttribute( stmt, field_index + 1, SQL_DESC_CONCISE_TYPE, NULL, 0, NULL, &sql_field_type TSRMLS_CC ); - - // Get the length of the field. - core::SQLColAttribute( stmt, field_index + 1, SQL_DESC_LENGTH, NULL, 0, NULL, &sql_field_len TSRMLS_CC ); + sqlsrv_phptype sqlsrv_php_type = sqlsrv_php_type_in; - // Get the corresponding php type from the sql type. - sqlsrv_php_type = stmt->sql_type_to_php_type( static_cast( sql_field_type ), static_cast( sql_field_len ), prefer_string ); - } + SQLLEN sql_field_type = 0; + SQLLEN sql_field_len = 0; - // Verify that we have an acceptable type to convert. - CHECK_CUSTOM_ERROR( !is_valid_sqlsrv_phptype( sqlsrv_php_type ), stmt, SQLSRV_ERROR_INVALID_TYPE ) { - throw core::CoreException(); - } - - if( sqlsrv_php_type_out != NULL ) - *sqlsrv_php_type_out = static_cast( sqlsrv_php_type.typeinfo.type ); + // Make sure that the statement was executed and not just prepared. + CHECK_CUSTOM_ERROR( !stmt->executed, stmt, SQLSRV_ERROR_STATEMENT_NOT_EXECUTED ) { + throw core::CoreException(); + } - // Retrieve the data - core_get_field_common( stmt, field_index, sqlsrv_php_type, field_value, field_len TSRMLS_CC ); - - // 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 ); - core::sqlsrv_zend_hash_index_update_mem( *stmt, Z_ARRVAL( stmt->field_cache ), field_index, &cache, sizeof(field_cache) TSRMLS_CC ); - } - } + // if the field is to be cached, and this field is being retrieved out of order, cache prior fields so they + // may also be retrieved. + if( cache_field && (field_index - stmt->last_field_index ) >= 2 ) { + sqlsrv_phptype invalid; + invalid.typeinfo.type = SQLSRV_PHPTYPE_INVALID; + for( int i = stmt->last_field_index + 1; i < field_index; ++i ) { + SQLSRV_ASSERT((cached = reinterpret_cast(zend_hash_index_find_ptr(Z_ARRVAL(stmt->field_cache), i))) == NULL, + "Field already cached." ); + 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; + *field_len = 0; + } + } + } - catch( core::CoreException& e) { - throw e; - } + // If the php type was not specified set the php type to be the default type. + if( sqlsrv_php_type.typeinfo.type == SQLSRV_PHPTYPE_INVALID ) { + + // Get the SQL type of the field. + core::SQLColAttribute( stmt, field_index + 1, SQL_DESC_CONCISE_TYPE, NULL, 0, NULL, &sql_field_type TSRMLS_CC ); + + // Get the length of the field. + core::SQLColAttribute( stmt, field_index + 1, SQL_DESC_LENGTH, NULL, 0, NULL, &sql_field_len TSRMLS_CC ); + + // Get the corresponding php type from the sql type. + sqlsrv_php_type = stmt->sql_type_to_php_type( static_cast( sql_field_type ), static_cast( sql_field_len ), prefer_string ); + } + + // Verify that we have an acceptable type to convert. + CHECK_CUSTOM_ERROR( !is_valid_sqlsrv_phptype( sqlsrv_php_type ), stmt, SQLSRV_ERROR_INVALID_TYPE ) { + throw core::CoreException(); + } + + if( sqlsrv_php_type_out != NULL ) + *sqlsrv_php_type_out = static_cast( sqlsrv_php_type.typeinfo.type ); + + // Retrieve the data + core_get_field_common( stmt, field_index, sqlsrv_php_type, field_value, field_len TSRMLS_CC ); + + // 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 ); + core::sqlsrv_zend_hash_index_update_mem( *stmt, Z_ARRVAL( stmt->field_cache ), field_index, &cache, sizeof(field_cache) TSRMLS_CC ); + } + } + + catch( core::CoreException& e ) { + throw e; + } } // core_sqlsrv_has_any_result @@ -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." ); } @@ -1470,191 +1468,191 @@ size_t calc_utf8_missing( sqlsrv_stmt* stmt, const char* buffer, size_t buffer_e // The memory allocation has to happen in the core layer because otherwise // 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 ) -{ - try { +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 ) +{ + try { - close_active_stream( stmt TSRMLS_CC ); + close_active_stream( stmt TSRMLS_CC ); - // make sure that fetch is called before trying to retrieve. - CHECK_CUSTOM_ERROR( !stmt->fetch_called, stmt, SQLSRV_ERROR_FETCH_NOT_CALLED ) { - throw core::CoreException(); - } + // make sure that fetch is called before trying to retrieve. + CHECK_CUSTOM_ERROR( !stmt->fetch_called, stmt, SQLSRV_ERROR_FETCH_NOT_CALLED ) { + throw core::CoreException(); + } - // make sure that fields are not retrieved incorrectly. - CHECK_CUSTOM_ERROR( stmt->last_field_index > field_index, stmt, SQLSRV_ERROR_FIELD_INDEX_ERROR, field_index, - stmt->last_field_index ) { - throw core::CoreException(); - } + // make sure that fields are not retrieved incorrectly. + CHECK_CUSTOM_ERROR( stmt->last_field_index > field_index, stmt, SQLSRV_ERROR_FIELD_INDEX_ERROR, field_index, + stmt->last_field_index ) { + throw core::CoreException(); + } - switch( sqlsrv_php_type.typeinfo.type ) { - - case SQLSRV_PHPTYPE_INT: - { - sqlsrv_malloc_auto_ptr field_value_temp; - field_value_temp = static_cast( sqlsrv_malloc( sizeof( long ))); + switch( sqlsrv_php_type.typeinfo.type ) { - SQLRETURN r = stmt->current_results->get_data(field_index + 1, SQL_C_LONG, field_value_temp, sizeof( long ), - field_len, true /*handle_warning*/ TSRMLS_CC ); - - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw core::CoreException(); - } + case SQLSRV_PHPTYPE_INT: + { + sqlsrv_malloc_auto_ptr field_value_temp; + field_value_temp = static_cast( sqlsrv_malloc( sizeof( long ))); - CHECK_CUSTOM_ERROR( (r == SQL_NO_DATA), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { - throw core::CoreException(); - } + SQLRETURN r = stmt->current_results->get_data( field_index + 1, SQL_C_LONG, field_value_temp, sizeof( long ), + field_len, true /*handle_warning*/ TSRMLS_CC ); - if( *field_len == SQL_NULL_DATA ) { - *field_value = NULL; - break; - } - - *field_value = field_value_temp; - field_value_temp.transferred(); - break; - } + CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { + throw core::CoreException(); + } - case SQLSRV_PHPTYPE_FLOAT: - { - sqlsrv_malloc_auto_ptr field_value_temp; - field_value_temp = static_cast( sqlsrv_malloc( sizeof( double ))); + CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { + throw core::CoreException(); + } - SQLRETURN r = stmt->current_results->get_data( field_index + 1, SQL_C_DOUBLE, field_value_temp, sizeof( double ), - field_len, true /*handle_warning*/ TSRMLS_CC ); - - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw core::CoreException(); - } + if( *field_len == SQL_NULL_DATA ) { + field_value = NULL; + break; + } - CHECK_CUSTOM_ERROR( (r == SQL_NO_DATA), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { - throw core::CoreException (); - } + field_value = field_value_temp; + field_value_temp.transferred(); + break; + } - if( *field_len == SQL_NULL_DATA ) { - *field_value = NULL; - break; - } - - *field_value = field_value_temp; - field_value_temp.transferred(); - break; - } - - case SQLSRV_PHPTYPE_STRING: - { - get_field_as_string( stmt, field_index, sqlsrv_php_type, field_value, field_len TSRMLS_CC ); - break; - } - - // get the date as a string (http://msdn2.microsoft.com/en-us/library/ms712387(VS.85).aspx) and - // convert it to a DateTime object and return the created object - case SQLSRV_PHPTYPE_DATETIME: - { - char field_value_temp[ MAX_DATETIME_STRING_LEN ]; - zval params[1]; - zval field_value_temp_z; - zval function_z; - zval_auto_ptr return_value_z; - - ZVAL_UNDEF( &field_value_temp_z ); - ZVAL_UNDEF( &function_z ); - ZVAL_UNDEF( params ); - return_value_z = (zval *)sqlsrv_malloc( sizeof(zval) ); - ZVAL_UNDEF( return_value_z ); - - SQLRETURN r = stmt->current_results->get_data( field_index + 1, SQL_C_CHAR, field_value_temp, - MAX_DATETIME_STRING_LEN, field_len, true TSRMLS_CC ); + case SQLSRV_PHPTYPE_FLOAT: + { + sqlsrv_malloc_auto_ptr field_value_temp; + field_value_temp = static_cast( sqlsrv_malloc( sizeof( double ))); + + SQLRETURN r = stmt->current_results->get_data( field_index + 1, SQL_C_DOUBLE, field_value_temp, sizeof( double ), + field_len, true /*handle_warning*/ TSRMLS_CC ); - CHECK_CUSTOM_ERROR( (r == SQL_NO_DATA), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { - throw core::CoreException (); - } + CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { + throw core::CoreException(); + } - if( *field_len == SQL_NULL_DATA ) { - ZVAL_NULL( return_value_z ); - *field_value = reinterpret_cast( return_value_z.get() ); - return_value_z.transferred(); - break; - } - - // Convert the string date to a DateTime object - core::sqlsrv_zval_stringl( &field_value_temp_z, field_value_temp, *field_len ); - core::sqlsrv_zval_stringl( &function_z, "date_create", sizeof("date_create") -1 ); - params[0] = field_value_temp_z; + CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { + throw core::CoreException(); + } - if( call_user_function( EG( function_table ), NULL, &function_z, return_value_z, 1, - params TSRMLS_CC ) == FAILURE ) { - THROW_CORE_ERROR( stmt, SQLSRV_ERROR_DATETIME_CONVERSION_FAILED ); - } - - *field_value = reinterpret_cast( return_value_z.get() ); - return_value_z.transferred(); - break; - } - - // create a stream wrapper around the field and return that object to the PHP script. calls to fread - // on the stream will result in calls to SQLGetData. This is handled in stream.cpp. See that file - // for how these fields are used. - case SQLSRV_PHPTYPE_STREAM: - { - - zval_auto_ptr return_value_z; - return_value_z = (zval *)sqlsrv_malloc(sizeof(zval)); - ZVAL_UNDEF(return_value_z); - php_stream* stream = NULL; - sqlsrv_stream* ss = NULL; - SQLLEN sql_type; + if( *field_len == SQL_NULL_DATA ) { + field_value = NULL; + break; + } - SQLRETURN r = SQLColAttribute( stmt->handle(), field_index + 1, SQL_DESC_TYPE, NULL, 0, NULL, &sql_type ); - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw core::CoreException(); - } - - CHECK_CUSTOM_ERROR( !is_streamable_type( sql_type ), stmt, SQLSRV_ERROR_STREAMABLE_TYPES_ONLY ) { - throw core::CoreException(); - } - - stream = php_stream_open_wrapper( "sqlsrv://sqlncli10", "r", 0, NULL ); + field_value = field_value_temp; + field_value_temp.transferred(); + break; + } - CHECK_CUSTOM_ERROR( !stream, stmt, SQLSRV_ERROR_STREAM_CREATE ) { - throw core::CoreException(); - } + case SQLSRV_PHPTYPE_STRING: + { + get_field_as_string( stmt, field_index, sqlsrv_php_type, field_value, field_len TSRMLS_CC ); + break; + } - ss = static_cast( stream->abstract ); - ss->stmt = stmt; - ss->field_index = field_index; - ss->sql_type = static_cast( sql_type ); - ss->encoding = static_cast( sqlsrv_php_type.typeinfo.encoding ); + // get the date as a string (http://msdn2.microsoft.com/en-us/library/ms712387(VS.85).aspx) and + // convert it to a DateTime object and return the created object + case SQLSRV_PHPTYPE_DATETIME: + { + char field_value_temp[ MAX_DATETIME_STRING_LEN ]; + zval params[1]; + zval field_value_temp_z; + zval function_z; + zval_auto_ptr return_value_z; - // turn our stream into a zval to be returned - php_stream_to_zval( stream, return_value_z ); + ZVAL_UNDEF( &field_value_temp_z ); + ZVAL_UNDEF( &function_z ); + ZVAL_UNDEF( params ); + return_value_z = (zval *)sqlsrv_malloc( sizeof( zval )); + ZVAL_UNDEF( return_value_z ); - // mark this as our active stream - stmt->active_stream = return_value_z; - *field_value = reinterpret_cast( return_value_z.get() ); - return_value_z.transferred(); - break; - } + SQLRETURN r = stmt->current_results->get_data( field_index + 1, SQL_C_CHAR, field_value_temp, + MAX_DATETIME_STRING_LEN, field_len, true TSRMLS_CC ); - case SQLSRV_PHPTYPE_NULL: - *field_value = NULL; - *field_len = 0; - break; + CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { + throw core::CoreException(); + } - default: - DIE( "core_get_field_common: Unexpected sqlsrv_phptype provided" ); - break; - } + if( *field_len == SQL_NULL_DATA ) { + ZVAL_NULL( return_value_z ); + field_value = reinterpret_cast( return_value_z.get()); + return_value_z.transferred(); + break; + } - // sucessfully retrieved the field, so update our last retrieved field - if( stmt->last_field_index < field_index ) { - stmt->last_field_index = field_index; - } - } - catch( core::CoreException& e ) { - throw e; - } + // Convert the string date to a DateTime object + core::sqlsrv_zval_stringl( &field_value_temp_z, field_value_temp, *field_len ); + core::sqlsrv_zval_stringl( &function_z, "date_create", sizeof("date_create") - 1 ); + params[0] = field_value_temp_z; + + if( call_user_function( EG( function_table ), NULL, &function_z, return_value_z, 1, + params TSRMLS_CC ) == FAILURE) { + THROW_CORE_ERROR(stmt, SQLSRV_ERROR_DATETIME_CONVERSION_FAILED); + } + + field_value = reinterpret_cast( 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; + } + + // create a stream wrapper around the field and return that object to the PHP script. calls to fread + // on the stream will result in calls to SQLGetData. This is handled in stream.cpp. See that file + // for how these fields are used. + case SQLSRV_PHPTYPE_STREAM: + { + + zval_auto_ptr return_value_z; + return_value_z = (zval *)sqlsrv_malloc(sizeof(zval)); + ZVAL_UNDEF(return_value_z); + php_stream* stream = NULL; + sqlsrv_stream* ss = NULL; + SQLLEN sql_type; + + SQLRETURN r = SQLColAttribute( stmt->handle(), field_index + 1, SQL_DESC_TYPE, NULL, 0, NULL, &sql_type ); + CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { + throw core::CoreException(); + } + + CHECK_CUSTOM_ERROR( !is_streamable_type( sql_type ), stmt, SQLSRV_ERROR_STREAMABLE_TYPES_ONLY ) { + throw core::CoreException(); + } + + stream = php_stream_open_wrapper( "sqlsrv://sqlncli10", "r", 0, NULL ); + + CHECK_CUSTOM_ERROR( !stream, stmt, SQLSRV_ERROR_STREAM_CREATE ) { + throw core::CoreException(); + } + + ss = static_cast( stream->abstract ); + ss->stmt = stmt; + ss->field_index = field_index; + ss->sql_type = static_cast( sql_type ); + ss->encoding = static_cast( sqlsrv_php_type.typeinfo.encoding ); + + // turn our stream into a zval to be returned + php_stream_to_zval( stream, return_value_z ); + + field_value = reinterpret_cast( return_value_z.get()); + return_value_z.transferred(); + break; + } + + case SQLSRV_PHPTYPE_NULL: + field_value = NULL; + *field_len = 0; + break; + + default: + DIE( "core_get_field_common: Unexpected sqlsrv_phptype provided" ); + break; + } + + // sucessfully retrieved the field, so update our last retrieved field + if( stmt->last_field_index < field_index ) { + stmt->last_field_index = field_index; + } + } + catch( core::CoreException& e ) { + throw e; + } } @@ -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( output_param_temp ); zval* value_z = Z_REFVAL_P( output_param->param_z ); switch( Z_TYPE_P( value_z )) { case IS_STRING: @@ -2037,241 +2036,241 @@ 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 )); return; } -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 ) +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 ) { - SQLRETURN r; - SQLSMALLINT c_type; - SQLLEN sql_field_type = 0; - SQLSMALLINT extra = 0; - SQLLEN field_len_temp; - SQLLEN sql_display_size = 0; - char* field_value_temp = NULL; + SQLRETURN r; + SQLSMALLINT c_type; + SQLLEN sql_field_type = 0; + SQLSMALLINT extra = 0; + SQLLEN field_len_temp; + SQLLEN sql_display_size = 0; + char* field_value_temp = NULL; - try { + try { - DEBUG_SQLSRV_ASSERT( sqlsrv_php_type.typeinfo.type == SQLSRV_PHPTYPE_STRING, - "Type should be SQLSRV_PHPTYPE_STRING in get_field_as_string" ); - - if( sqlsrv_php_type.typeinfo.encoding == SQLSRV_ENCODING_DEFAULT ) { - sqlsrv_php_type.typeinfo.encoding = stmt->conn->encoding(); - } + DEBUG_SQLSRV_ASSERT( sqlsrv_php_type.typeinfo.type == SQLSRV_PHPTYPE_STRING, + "Type should be SQLSRV_PHPTYPE_STRING in get_field_as_string" ); - // Set the C type and account for null characters at the end of the data. - switch( sqlsrv_php_type.typeinfo.encoding ) { - case CP_UTF8: - c_type = SQL_C_WCHAR; - extra = sizeof( SQLWCHAR ); - break; - case SQLSRV_ENCODING_BINARY: - c_type = SQL_C_BINARY; - extra = 0; - break; - default: - c_type = SQL_C_CHAR; - extra = sizeof( SQLCHAR ); - break; - } + if( sqlsrv_php_type.typeinfo.encoding == SQLSRV_ENCODING_DEFAULT ) { + sqlsrv_php_type.typeinfo.encoding = stmt->conn->encoding(); + } - // Get the SQL type of the field. - core::SQLColAttribute( stmt, field_index + 1, SQL_DESC_CONCISE_TYPE, NULL, 0, NULL, &sql_field_type TSRMLS_CC ); + // Set the C type and account for null characters at the end of the data. + switch( sqlsrv_php_type.typeinfo.encoding ) { + case CP_UTF8: + c_type = SQL_C_WCHAR; + extra = sizeof( SQLWCHAR ); + break; + case SQLSRV_ENCODING_BINARY: + c_type = SQL_C_BINARY; + extra = 0; + break; + default: + c_type = SQL_C_CHAR; + extra = sizeof( SQLCHAR ); + break; + } - // Calculate the field size. - calc_string_size( stmt, field_index, sql_field_type, sql_display_size TSRMLS_CC ); + // Get the SQL type of the field. + core::SQLColAttribute( stmt, field_index + 1, SQL_DESC_CONCISE_TYPE, NULL, 0, NULL, &sql_field_type TSRMLS_CC ); - // if this is a large type, then read the first few bytes to get the actual length from SQLGetData - if( sql_display_size == 0 || sql_display_size == LONG_MAX || - sql_display_size == LONG_MAX >> 1 || sql_display_size == ULONG_MAX - 1 ) { + // Calculate the field size. + calc_string_size( stmt, field_index, sql_field_type, sql_display_size TSRMLS_CC ); - field_len_temp = INITIAL_FIELD_STRING_LEN; - - field_value_temp = static_cast( sqlsrv_malloc( field_len_temp + extra + 1 )); - - r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp, ( field_len_temp + extra ), - &field_len_temp, false /*handle_warning*/ TSRMLS_CC ); - - CHECK_CUSTOM_ERROR( (r == SQL_NO_DATA), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { - throw core::CoreException (); - } + // if this is a large type, then read the first few bytes to get the actual length from SQLGetData + if( sql_display_size == 0 || sql_display_size == LONG_MAX || + sql_display_size == LONG_MAX >> 1 || sql_display_size == ULONG_MAX - 1 ) { - if( field_len_temp == SQL_NULL_DATA ) { - *field_value = NULL; - sqlsrv_free( field_value_temp ); - return; - } + field_len_temp = INITIAL_FIELD_STRING_LEN; - if( r == SQL_SUCCESS_WITH_INFO ) { + field_value_temp = static_cast( sqlsrv_malloc( field_len_temp + extra + 1 )); + + r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp, ( field_len_temp + extra ), + &field_len_temp, false /*handle_warning*/ TSRMLS_CC ); + + CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { + throw core::CoreException(); + } + + if( field_len_temp == SQL_NULL_DATA ) { + field_value = NULL; + sqlsrv_free( field_value_temp ); + return; + } + + if( r == SQL_SUCCESS_WITH_INFO ) { + + SQLCHAR state[ SQL_SQLSTATE_BUFSIZE ]; + SQLSMALLINT len; + + stmt->current_results->get_diag_field( 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len TSRMLS_CC ); + + if( is_truncated_warning( state )) { - SQLCHAR state[ SQL_SQLSTATE_BUFSIZE ]; - SQLSMALLINT len; - - stmt->current_results->get_diag_field( 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len TSRMLS_CC ); - - if( is_truncated_warning( state ) ) { - SQLLEN dummy_field_len; - // for XML (and possibly other conditions) the field length returned is not the real field length, so - // in every pass, we double the allocation size to retrieve all the contents. - if( field_len_temp == SQL_NO_TOTAL ) { - - // reset the field_len_temp - field_len_temp = INITIAL_FIELD_STRING_LEN; + // for XML (and possibly other conditions) the field length returned is not the real field length, so + // in every pass, we double the allocation size to retrieve all the contents. + if( field_len_temp == SQL_NO_TOTAL ) { - do { - SQLLEN initial_field_len = field_len_temp; - // Double the size. - field_len_temp *= 2; - - field_value_temp = static_cast( sqlsrv_realloc( field_value_temp, field_len_temp + extra + 1 )); - - field_len_temp -= initial_field_len; + // reset the field_len_temp + field_len_temp = INITIAL_FIELD_STRING_LEN; - // Get the rest of the data. - r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp + initial_field_len, - field_len_temp + extra, &dummy_field_len, - false /*handle_warning*/ TSRMLS_CC ); + do { + SQLLEN initial_field_len = field_len_temp; + // Double the size. + field_len_temp *= 2; - // the last packet will contain the actual amount retrieved, not SQL_NO_TOTAL - // so we calculate the actual length of the string with that. - if( dummy_field_len != SQL_NO_TOTAL ) - field_len_temp += dummy_field_len; - else - field_len_temp += initial_field_len; + field_value_temp = static_cast( sqlsrv_realloc( field_value_temp, field_len_temp + extra + 1 )); - if( r == SQL_SUCCESS_WITH_INFO ) { - core::SQLGetDiagField( stmt, 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len - TSRMLS_CC ); - } + field_len_temp -= initial_field_len; - } while( r == SQL_SUCCESS_WITH_INFO && is_truncated_warning( state )); - } - else { + // Get the rest of the data. + r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp + initial_field_len, + field_len_temp + extra, &dummy_field_len, + false /*handle_warning*/ TSRMLS_CC ); - // We got the field_len_temp from SQLGetData call. - field_value_temp = static_cast( sqlsrv_realloc( field_value_temp, field_len_temp + extra + 1 )); - - // We have already recieved INITIAL_FIELD_STRING_LEN size data. - field_len_temp -= INITIAL_FIELD_STRING_LEN; - - // Get the rest of the data. - r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp + INITIAL_FIELD_STRING_LEN, - field_len_temp + extra, &dummy_field_len, - true /*handle_warning*/ TSRMLS_CC ); + // the last packet will contain the actual amount retrieved, not SQL_NO_TOTAL + // so we calculate the actual length of the string with that. + if( dummy_field_len != SQL_NO_TOTAL ) + field_len_temp += dummy_field_len; + else + field_len_temp += initial_field_len; - if( dummy_field_len == SQL_NULL_DATA ) { - *field_value = NULL; - sqlsrv_free( field_value_temp ); - return; - } - - CHECK_CUSTOM_ERROR( (r == SQL_NO_DATA), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { - throw core::CoreException (); - } - - field_len_temp += INITIAL_FIELD_STRING_LEN; - } + if( r == SQL_SUCCESS_WITH_INFO ) { + core::SQLGetDiagField( stmt, 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len + TSRMLS_CC ); + } - } // if( is_truncation_warning ( state ) ) - else { - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw core::CoreException(); - } - } - } // if( r == SQL_SUCCESS_WITH_INFO ) + } while( r == SQL_SUCCESS_WITH_INFO && is_truncated_warning( state )); + } + else { - if( sqlsrv_php_type.typeinfo.encoding == SQLSRV_ENCODING_UTF8 ) { + // We got the field_len_temp from SQLGetData call. + field_value_temp = static_cast( sqlsrv_realloc( field_value_temp, field_len_temp + extra + 1 )); - bool converted = convert_string_from_utf16_inplace( static_cast( sqlsrv_php_type.typeinfo.encoding ), - &field_value_temp, field_len_temp ); - - CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, get_last_error_message() ) { - throw core::CoreException (); - } - } - } // if ( sql_display_size == 0 || sql_display_size == LONG_MAX .. ) - - else if( sql_display_size >= 1 && sql_display_size <= SQL_SERVER_MAX_FIELD_SIZE ) { + // We have already recieved INITIAL_FIELD_STRING_LEN size data. + field_len_temp -= INITIAL_FIELD_STRING_LEN; - // only allow binary retrievals for char and binary types. All others get a string converted - // to the encoding type they asked for. - - // null terminator - if( c_type == SQL_C_CHAR ) { - sql_display_size += sizeof( SQLCHAR ); - } + // Get the rest of the data. + r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp + INITIAL_FIELD_STRING_LEN, + field_len_temp + extra, &dummy_field_len, + true /*handle_warning*/ TSRMLS_CC ); - // For WCHAR multiply by sizeof(WCHAR) and include the null terminator - else if( c_type == SQL_C_WCHAR ) { - sql_display_size = (sql_display_size * sizeof(WCHAR)) + sizeof(WCHAR); - } + if( dummy_field_len == SQL_NULL_DATA ) { + field_value = NULL; + sqlsrv_free( field_value_temp ); + return; + } - field_value_temp = static_cast( sqlsrv_malloc( sql_display_size + extra + 1 )); - - // get the data - r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp, sql_display_size, - &field_len_temp, true /*handle_warning*/ TSRMLS_CC ); - CHECK_SQL_ERROR( r, stmt ) { - throw core::CoreException(); - } - CHECK_CUSTOM_ERROR( (r == SQL_NO_DATA), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { - throw core::CoreException (); - } + CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { + throw core::CoreException(); + } - if( field_len_temp == SQL_NULL_DATA ) { - *field_value = NULL; - sqlsrv_free( field_value_temp ); - return; - } - - if( sqlsrv_php_type.typeinfo.encoding == CP_UTF8 ) { + field_len_temp += INITIAL_FIELD_STRING_LEN; + } - bool converted = convert_string_from_utf16_inplace( static_cast( sqlsrv_php_type.typeinfo.encoding ), - &field_value_temp, field_len_temp ); - - CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, get_last_error_message() ) { - throw core::CoreException (); - } - } - } // else if( sql_display_size >= 1 && sql_display_size <= SQL_SERVER_MAX_FIELD_SIZE ) + } // if( is_truncation_warning ( state ) ) + else { + CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { + throw core::CoreException(); + } + } + } // if( r == SQL_SUCCESS_WITH_INFO ) - else { - - DIE( "Invalid sql_display_size" ); - return; // to eliminate a warning - } - - *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 - // runtime checks to see if a string is null terminated and issues a warning about it if running in debug mode. - // SQL_C_BINARY fields don't return a NULL terminator, so we allocate an extra byte on each field and use the ternary - // operator to set add 1 to fill the null terminator - field_value_temp[field_len_temp] = '\0'; - } + if( sqlsrv_php_type.typeinfo.encoding == SQLSRV_ENCODING_UTF8 ) { - catch( core::CoreException& ) { + bool converted = convert_string_from_utf16_inplace( static_cast( sqlsrv_php_type.typeinfo.encoding ), + &field_value_temp, field_len_temp ); - *field_value = NULL; - *field_len = 0; - sqlsrv_free( field_value_temp ); - throw; - } - catch ( ... ) { + CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, get_last_error_message()) { + throw core::CoreException(); + } + } + } // if ( sql_display_size == 0 || sql_display_size == LONG_MAX .. ) - *field_value = NULL; - *field_len = 0; - sqlsrv_free( field_value_temp ); - throw; - } + else if( sql_display_size >= 1 && sql_display_size <= SQL_SERVER_MAX_FIELD_SIZE ) { + + // only allow binary retrievals for char and binary types. All others get a string converted + // to the encoding type they asked for. + + // null terminator + if( c_type == SQL_C_CHAR ) { + sql_display_size += sizeof( SQLCHAR ); + } + + // For WCHAR multiply by sizeof(WCHAR) and include the null terminator + else if( c_type == SQL_C_WCHAR ) { + sql_display_size = (sql_display_size * sizeof(WCHAR)) + sizeof(WCHAR); + } + + field_value_temp = static_cast( sqlsrv_malloc( sql_display_size + extra + 1 )); + + // get the data + r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp, sql_display_size, + &field_len_temp, true /*handle_warning*/ TSRMLS_CC ); + CHECK_SQL_ERROR( r, stmt ) { + throw core::CoreException(); + } + CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { + throw core::CoreException(); + } + + if( field_len_temp == SQL_NULL_DATA ) { + field_value = NULL; + sqlsrv_free( field_value_temp ); + return; + } + + if( sqlsrv_php_type.typeinfo.encoding == CP_UTF8 ) { + + bool converted = convert_string_from_utf16_inplace( static_cast( sqlsrv_php_type.typeinfo.encoding ), + &field_value_temp, field_len_temp ); + + CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, get_last_error_message()) { + throw core::CoreException(); + } + } + } // else if( sql_display_size >= 1 && sql_display_size <= SQL_SERVER_MAX_FIELD_SIZE ) + + else { + + DIE( "Invalid sql_display_size" ); + return; // to eliminate a warning + } + + 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 + // runtime checks to see if a string is null terminated and issues a warning about it if running in debug mode. + // SQL_C_BINARY fields don't return a NULL terminator, so we allocate an extra byte on each field and use the ternary + // operator to set add 1 to fill the null terminator + field_value_temp[field_len_temp] = '\0'; + } + + catch( core::CoreException& ) { + + field_value = NULL; + *field_len = 0; + sqlsrv_free( field_value_temp ); + throw; + } + catch ( ... ) { + + field_value = NULL; + *field_len = 0; + sqlsrv_free( field_value_temp ); + throw; + } } diff --git a/pdo_sqlsrv/core_stream.cpp b/pdo_sqlsrv/core_stream.cpp index 17468d8c..a709c186 100644 --- a/pdo_sqlsrv/core_stream.cpp +++ b/pdo_sqlsrv/core_stream.cpp @@ -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; diff --git a/pdo_sqlsrv/pdo_dbh.cpp b/pdo_sqlsrv/pdo_dbh.cpp index 968fa771..23e1cd99 100644 --- a/pdo_sqlsrv/pdo_dbh.cpp +++ b/pdo_sqlsrv/pdo_dbh.cpp @@ -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 )) { + size_t int_key = -1; + zend_string *key = NULL; + zval* data = NULL; - int type = HASH_KEY_NON_EXISTENT; - zend_string *key = NULL; - size_t int_key = -1; - zval* data; - int result = 0; + 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(); + } - type = zend_hash_get_current_key( options_ht, &key, &int_key); - 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 ); - } + add_stmt_option_key( ctx, int_key, pdo_stmt_options_ht, data TSRMLS_CC ); + } ZEND_HASH_FOREACH_END(); } } catch( core::CoreException& ) { diff --git a/pdo_sqlsrv/pdo_stmt.cpp b/pdo_sqlsrv/pdo_stmt.cpp index 74aaa992..a043c21f 100644 --- a/pdo_sqlsrv/pdo_stmt.cpp +++ b/pdo_sqlsrv/pdo_stmt.cpp @@ -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( ptr ), + core_sqlsrv_get_field( driver_stmt, colno, sqlsrv_php_type, false, *(reinterpret_cast(ptr)), reinterpret_cast( len ), true, &sqlsrv_phptype_out TSRMLS_CC ); - zval* zval_ptr = reinterpret_cast( 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( ptr ), *len ); *ptr = reinterpret_cast( zval_ptr ); diff --git a/sqlsrv/conn.cpp b/sqlsrv/conn.cpp index 998d50b3..0692a2ce 100644 --- a/sqlsrv/conn.cpp +++ b/sqlsrv/conn.cpp @@ -57,23 +57,23 @@ 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 )) { + zend_ulong index = -1; + zend_string* key = NULL; + void* ss_encoding_temp = NULL; - sqlsrv_encoding* ss_encoding = NULL; - core::sqlsrv_zend_hash_get_current_data_ptr( *conn, g_ss_encodings_ht,(void*&) ss_encoding TSRMLS_CC ); - - if( !strnicmp( encoding, ss_encoding->iana, encoding_len ) ) { + ZEND_HASH_FOREACH_KEY_PTR( g_ss_encodings_ht, index, key, ss_encoding_temp ) { + sqlsrv_encoding* ss_encoding = reinterpret_cast( ss_encoding_temp ); + ss_encoding_temp = NULL; + if (!strnicmp( encoding, ss_encoding->iana, encoding_len )) { - if( ss_encoding->not_for_connection ) { - THROW_SS_ERROR( conn, SS_SQLSRV_ERROR_CONNECT_ILLEGAL_ENCODING, encoding ); - } + if ( ss_encoding->not_for_connection ) { + THROW_SS_ERROR( conn, SS_SQLSRV_ERROR_CONNECT_ILLEGAL_ENCODING, encoding ); + } - conn->set_encoding( static_cast( ss_encoding->code_page )); - return; - } - } + conn->set_encoding( static_cast(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,45 +1085,41 @@ 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; - - 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 ); - } - catch( core::CoreException& ) { + zval* rsrc_ptr = NULL; + ZEND_HASH_FOREACH_VAL( conn->stmts, rsrc_ptr ) { + try { + 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 + // destroyed in sqlsrv_stmt_dtor. However, rather than die (assert), we simply skip this resource + // and move to the next one. + ss_sqlsrv_stmt* stmt = NULL; + stmt = static_cast( Z_RES_VAL_P( rsrc_ptr )); + if( stmt == NULL || Z_RES_TYPE_P( rsrc_ptr ) != ss_sqlsrv_stmt::descriptor ) { + LOG( SEV_ERROR, "Non existent statement found in connection. Statements should remove themselves" + " 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; - 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 - // destroyed in sqlsrv_stmt_dtor. However, rather than die (assert), we simply skip this resource - // and move to the next one. - ss_sqlsrv_stmt* stmt = NULL; - stmt = static_cast( Z_RES_VAL_P( rsrc_ptr )); - if (stmt == NULL || Z_RES_TYPE_P(rsrc_ptr) != ss_sqlsrv_stmt::descriptor) { - LOG( SEV_ERROR, "Non existent statement found in connection. Statements should remove themselves" - " 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; - - try { - // this would call the destructor on the statement. - int zr = zend_list_close(Z_RES_P(rsrc_ptr)); - } - catch( core::CoreException& ) { - LOG(SEV_ERROR, "Failed to remove statement resource %1!d! when closing the connection", Z_RES_HANDLE_P(rsrc_ptr)); - } - } + try { + // this would call the destructor on the statement. + int zr = zend_list_close(Z_RES_P(rsrc_ptr)); + } + 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,31 +1225,27 @@ 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 )) { + 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; - int type = HASH_KEY_NON_EXISTENT; - zend_string *key = NULL; - size_t key_len = 0; - zend_ulong int_key = -1; - zval* data = NULL; - zval* conn_opt = NULL; - int result = 0; + key_len = ZSTR_LEN( key ) + 1; + type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG; - type = zend_hash_get_current_key(options_ht, &key, &int_key); - key_len = ZSTR_LEN(key) + 1; - - if( type != HASH_KEY_IS_STRING ) { - std::ostringstream itoa; - itoa << int_key; - CHECK_CUSTOM_ERROR( true , ctx, SQLSRV_ERROR_INVALID_OPTION_KEY, itoa.str() ) { - 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 ); - } + if( type != HASH_KEY_IS_STRING ) { + std::ostringstream itoa; + itoa << int_key; + CHECK_CUSTOM_ERROR( true, ctx, SQLSRV_ERROR_INVALID_OPTION_KEY, itoa.str() ) { + throw core::CoreException(); + } + } + add_stmt_option_key( ctx, key, key_len, ss_stmt_options_ht, data TSRMLS_CC ); + } ZEND_HASH_FOREACH_END(); } } catch( core::CoreException& ) { @@ -1274,39 +1265,37 @@ 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 )) { + 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; - int type = HASH_KEY_NON_EXISTENT; - zend_string *key = NULL; - zend_ulong int_key = -1; - zval* data = NULL; + 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(); + } - type = zend_hash_get_current_key(options_ht, &key, &int_key); - - 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 + CHECK_CUSTOM_ERROR(( type != HASH_KEY_IS_STRING ), ctx, SS_SQLSRV_ERROR_INVALID_CONNECTION_KEY ) { + throw ss::SSException(); + } + + // 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 )) { + if( key_len == sizeof(SSConnOptionNames::UID) && !stricmp(ZSTR_VAL(key), SSConnOptionNames::UID )) { - *uid = Z_STRVAL_P( data ); - } + *uid = Z_STRVAL_P( data ); + } - else if(key_len == sizeof( SSConnOptionNames::PWD ) && !stricmp( ZSTR_VAL ( key ), SSConnOptionNames::PWD )) { - - *pwd = Z_STRVAL_P( data ); - } - else { + else if( key_len == sizeof( SSConnOptionNames::PWD ) && !stricmp( ZSTR_VAL( key ), SSConnOptionNames::PWD )) { - ::add_conn_option_key( ctx, key, key_len, ss_conn_options_ht, data TSRMLS_CC ); - } - } + *pwd = Z_STRVAL_P( data ); + } + else { + + ::add_conn_option_key( ctx, key, key_len, ss_conn_options_ht, data TSRMLS_CC ); + } + } ZEND_HASH_FOREACH_END(); } } catch( core::CoreException& ) { diff --git a/sqlsrv/core_conn.cpp b/sqlsrv/core_conn.cpp index 825393e7..2c8f423a 100644 --- a/sqlsrv/core_conn.cpp +++ b/sqlsrv/core_conn.cpp @@ -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,39 +124,49 @@ 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 ); - build_connection_string_and_set_conn_attr( conn, server, uid, pwd, options_ht, valid_conn_opts, driver, - conn_str TSRMLS_CC ); - - // We only support UTF-8 encoding for connection string. - // Convert our UTF-8 connection string to UTF-16 before connecting with SQLDriverConnnectW - wconn_len = static_cast( conn_str.length() + 1 ) * sizeof( wchar_t ); + // We only support UTF-8 encoding for connection string. + // Convert our UTF-8 connection string to UTF-16 before connecting with SQLDriverConnnectW + wconn_len = static_cast( conn_str.length() + 1 ) * sizeof( wchar_t ); - wconn_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_UTF8, conn_str.c_str(), static_cast( conn_str.length() ), &wconn_len ); - CHECK_CUSTOM_ERROR( wconn_string == NULL, conn, SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE, get_last_error_message() ) - { - throw core::CoreException(); - } - - SQLSMALLINT output_conn_size; - r = SQLDriverConnectW( conn->handle(), NULL, reinterpret_cast( wconn_string.get() ), - static_cast( wconn_len ), NULL, - 0, &output_conn_size, SQL_DRIVER_NOPROMPT ); - // clear the connection string from memory to remove sensitive data (such as a password). - memset( const_cast( conn_str.c_str()), 0, conn_str.size() ); - memset( wconn_string, 0, wconn_len * sizeof( wchar_t )); // wconn_len is the number of characters, not bytes - conn_str.clear(); + wconn_string = utf16_string_from_mbcs_string(SQLSRV_ENCODING_UTF8, conn_str.c_str(), static_cast(conn_str.length()), &wconn_len); + CHECK_CUSTOM_ERROR( wconn_string == NULL, conn, SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE, get_last_error_message()) + { + throw core::CoreException(); + } - if( !SQL_SUCCEEDED( r )) { - SQLCHAR state[ SQL_SQLSTATE_BUFSIZE ]; - SQLSMALLINT len; - SQLRETURN r = SQLGetDiagField( SQL_HANDLE_DBC, conn->handle(), 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len ); - // 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() ) { - throw core::CoreException(); - } - } + SQLSMALLINT output_conn_size; + r = SQLDriverConnectW( conn->handle(), NULL, reinterpret_cast( wconn_string.get()), + static_cast( wconn_len ), NULL, + 0, &output_conn_size, SQL_DRIVER_NOPROMPT ); + // clear the connection string from memory to remove sensitive data (such as a password). + memset( const_cast( conn_str.c_str()), 0, conn_str.size() ); + memset( wconn_string, 0, wconn_len * sizeof( wchar_t )); // wconn_len is the number of characters, not bytes + conn_str.clear(); + + if( !SQL_SUCCEEDED( r )) { + 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( 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( i ); + break; + } + + } CHECK_SQL_ERROR( r, conn ) { throw core::CoreException(); } @@ -168,7 +178,7 @@ sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_ // determine the version of the server we're connected to. The server version is left in the // connection upon return. determine_server_version( conn TSRMLS_CC ); - + } catch( std::bad_alloc& ) { memset( const_cast( conn_str.c_str()), 0, conn_str.size() ); @@ -532,9 +542,7 @@ void build_connection_string_and_set_conn_attr( sqlsrv_conn* conn, const char* s int zr = SUCCESS; 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,30 +594,25 @@ 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; - zval* data = NULL; + zend_string *key = NULL; + zend_ulong index = -1; + zval* data = NULL; - type = zend_hash_get_current_key( options, &key, &index ); - - // 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 ); + 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; - conn_opt = get_connection_option( conn, index, valid_conn_opts TSRMLS_CC ); - - if( index == SQLSRV_CONN_OPTION_MARS ) { - mars_mentioned = true; - } - - conn_opt->func( conn_opt, data, conn, connection_string TSRMLS_CC ); - } + // 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." ); + + conn_opt = get_connection_option( conn, index, valid_conn_opts TSRMLS_CC ); + + if( index == SQLSRV_CONN_OPTION_MARS ) { + mars_mentioned = true; + } + + 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"; diff --git a/sqlsrv/core_sqlsrv.h b/sqlsrv/core_sqlsrv.h index 2563ee70..840cea7f 100644 --- a/sqlsrv/core_sqlsrv.h +++ b/sqlsrv/core_sqlsrv.h @@ -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,8 +1323,8 @@ 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 SQLSRV_PHPTYPE *sqlsrv_php_type_out TSRMLS_DC ); + __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 ); void core_sqlsrv_post_param( sqlsrv_stmt* stmt, zend_ulong paramno, zval* param_z TSRMLS_DC ); @@ -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( 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( err_msg ), CONNECTION_BUSY_ODBC_ERROR[driver_version] )) { THROW_CORE_ERROR( stmt, SQLSRV_ERROR_MARS_OFF ); } diff --git a/sqlsrv/core_stmt.cpp b/sqlsrv/core_stmt.cpp index e727262f..4937b5b3 100644 --- a/sqlsrv/core_stmt.cpp +++ b/sqlsrv/core_stmt.cpp @@ -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, @@ -91,8 +91,8 @@ void default_sql_type( sqlsrv_stmt* stmt, SQLULEN paramno, zval* param_z, SQLSRV __out SQLSMALLINT& sql_type TSRMLS_DC ); 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 ); +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 ); 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, ¶m_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,32 +248,26 @@ 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_ulong index = -1; + zend_string *key = NULL; + zval* value_z = NULL; - zend_string *key = NULL; - zend_ulong index = -1; - zval* value_z = NULL; + ZEND_HASH_FOREACH_KEY_VAL( options_ht, index, key, value_z ) { - int type = zend_hash_get_current_key( options_ht, &key, &index); - - // 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. - // The driver layer should ensure that the key is valid. - DEBUG_SQLSRV_ASSERT( stmt_opt != NULL, "allocate_stmt: unexpected null value for statement option." ); - - // perform the actions the statement option needs done. - (*stmt_opt->func)( stmt, stmt_opt, value_z TSRMLS_CC ); - } + int type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG; - zend_hash_internal_pointer_end( options_ht ); + // The driver layer should ensure a valid key. + DEBUG_SQLSRV_ASSERT(( type == HASH_KEY_IS_LONG ), "allocate_stmt: Invalid statment option key provided." ); + + 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. + // The driver layer should ensure that the key is valid. + DEBUG_SQLSRV_ASSERT( stmt_opt != NULL, "allocate_stmt: unexpected null value for statement option." ); + + // perform the actions the statement option needs done. + (*stmt_opt->func)( stmt, stmt_opt, value_z TSRMLS_CC ); + } 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 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 - zend_hash_clean( Z_ARRVAL( stmt->param_streams )); - + if ( stmt->send_streams_at_exec ) { + zend_hash_clean( Z_ARRVAL( stmt->param_streams )); + } } catch( core::CoreException& e ) { @@ -869,100 +864,100 @@ 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 SQLSRV_PHPTYPE *sqlsrv_php_type_out TSRMLS_DC ) + __out void*& field_value, __out SQLLEN* field_len, bool cache_field, + __out SQLSRV_PHPTYPE *sqlsrv_php_type_out TSRMLS_DC) { - try { - - // close the stream to release the resource - close_active_stream( stmt TSRMLS_CC ); - - // if the field has been retrieved before, return the previous result - field_cache* cached = NULL; - if (NULL != (cached = reinterpret_cast(zend_hash_index_find_ptr(Z_ARRVAL(stmt->field_cache), static_cast(field_index))))) { - // the field value is NULL - if( cached->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 ); - if( cached->type.typeinfo.type == SQLSRV_PHPTYPE_STRING ) { - // prevent the 'string not null terminated' warning - reinterpret_cast( *field_value )[ cached->len ] = '\0'; - } - *field_len = cached->len; - if( sqlsrv_php_type_out ) { *sqlsrv_php_type_out = static_cast( cached->type.typeinfo.type ); } - } - return; - } - - sqlsrv_phptype sqlsrv_php_type = sqlsrv_php_type_in; + try { - SQLLEN sql_field_type = 0; - SQLLEN sql_field_len = 0; + // close the stream to release the resource + close_active_stream(stmt TSRMLS_CC); - // Make sure that the statement was executed and not just prepared. - CHECK_CUSTOM_ERROR( !stmt->executed, stmt, SQLSRV_ERROR_STATEMENT_NOT_EXECUTED ) { - throw core::CoreException(); - } + // if the field has been retrieved before, return the previous result + field_cache* cached = NULL; + if (NULL != ( cached = static_cast( zend_hash_index_find_ptr( Z_ARRVAL( stmt->field_cache ), static_cast( field_index ))))) { + // the field value is NULL + if( cached->value == NULL ) { + field_value = NULL; + *field_len = 0; + if( sqlsrv_php_type_out ) { *sqlsrv_php_type_out = SQLSRV_PHPTYPE_NULL; } + } + else { - // if the field is to be cached, and this field is being retrieved out of order, cache prior fields so they - // may also be retrieved. - if( cache_field && ( field_index - stmt->last_field_index ) >= 2 ) { - sqlsrv_phptype invalid; - invalid.typeinfo.type = SQLSRV_PHPTYPE_INVALID; - for( int i = stmt->last_field_index + 1; i < field_index; ++i ) { - SQLSRV_ASSERT((cached = reinterpret_cast(zend_hash_index_find_ptr(Z_ARRVAL(stmt->field_cache), i))) == NULL, - "Field already cached." ); - 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; - *field_len = 0; - } - } - } + 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( field_value )[ cached->len ] = '\0'; + } + *field_len = cached->len; + if( sqlsrv_php_type_out) { *sqlsrv_php_type_out = static_cast(cached->type.typeinfo.type); } + } + return; + } - // If the php type was not specified set the php type to be the default type. - if( sqlsrv_php_type.typeinfo.type == SQLSRV_PHPTYPE_INVALID ) { - - // Get the SQL type of the field. - core::SQLColAttribute( stmt, field_index + 1, SQL_DESC_CONCISE_TYPE, NULL, 0, NULL, &sql_field_type TSRMLS_CC ); - - // Get the length of the field. - core::SQLColAttribute( stmt, field_index + 1, SQL_DESC_LENGTH, NULL, 0, NULL, &sql_field_len TSRMLS_CC ); + sqlsrv_phptype sqlsrv_php_type = sqlsrv_php_type_in; - // Get the corresponding php type from the sql type. - sqlsrv_php_type = stmt->sql_type_to_php_type( static_cast( sql_field_type ), static_cast( sql_field_len ), prefer_string ); - } + SQLLEN sql_field_type = 0; + SQLLEN sql_field_len = 0; - // Verify that we have an acceptable type to convert. - CHECK_CUSTOM_ERROR( !is_valid_sqlsrv_phptype( sqlsrv_php_type ), stmt, SQLSRV_ERROR_INVALID_TYPE ) { - throw core::CoreException(); - } - - if( sqlsrv_php_type_out != NULL ) - *sqlsrv_php_type_out = static_cast( sqlsrv_php_type.typeinfo.type ); + // Make sure that the statement was executed and not just prepared. + CHECK_CUSTOM_ERROR( !stmt->executed, stmt, SQLSRV_ERROR_STATEMENT_NOT_EXECUTED ) { + throw core::CoreException(); + } - // Retrieve the data - core_get_field_common( stmt, field_index, sqlsrv_php_type, field_value, field_len TSRMLS_CC ); - - // 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 ); - core::sqlsrv_zend_hash_index_update_mem( *stmt, Z_ARRVAL( stmt->field_cache ), field_index, &cache, sizeof(field_cache) TSRMLS_CC ); - } - } + // if the field is to be cached, and this field is being retrieved out of order, cache prior fields so they + // may also be retrieved. + if( cache_field && (field_index - stmt->last_field_index ) >= 2 ) { + sqlsrv_phptype invalid; + invalid.typeinfo.type = SQLSRV_PHPTYPE_INVALID; + for( int i = stmt->last_field_index + 1; i < field_index; ++i ) { + SQLSRV_ASSERT((cached = reinterpret_cast(zend_hash_index_find_ptr(Z_ARRVAL(stmt->field_cache), i))) == NULL, + "Field already cached." ); + 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; + *field_len = 0; + } + } + } - catch( core::CoreException& e) { - throw e; - } + // If the php type was not specified set the php type to be the default type. + if( sqlsrv_php_type.typeinfo.type == SQLSRV_PHPTYPE_INVALID ) { + + // Get the SQL type of the field. + core::SQLColAttribute( stmt, field_index + 1, SQL_DESC_CONCISE_TYPE, NULL, 0, NULL, &sql_field_type TSRMLS_CC ); + + // Get the length of the field. + core::SQLColAttribute( stmt, field_index + 1, SQL_DESC_LENGTH, NULL, 0, NULL, &sql_field_len TSRMLS_CC ); + + // Get the corresponding php type from the sql type. + sqlsrv_php_type = stmt->sql_type_to_php_type( static_cast( sql_field_type ), static_cast( sql_field_len ), prefer_string ); + } + + // Verify that we have an acceptable type to convert. + CHECK_CUSTOM_ERROR( !is_valid_sqlsrv_phptype( sqlsrv_php_type ), stmt, SQLSRV_ERROR_INVALID_TYPE ) { + throw core::CoreException(); + } + + if( sqlsrv_php_type_out != NULL ) + *sqlsrv_php_type_out = static_cast( sqlsrv_php_type.typeinfo.type ); + + // Retrieve the data + core_get_field_common( stmt, field_index, sqlsrv_php_type, field_value, field_len TSRMLS_CC ); + + // 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 ); + core::sqlsrv_zend_hash_index_update_mem( *stmt, Z_ARRVAL( stmt->field_cache ), field_index, &cache, sizeof(field_cache) TSRMLS_CC ); + } + } + + catch( core::CoreException& e ) { + throw e; + } } // core_sqlsrv_has_any_result @@ -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." ); } @@ -1470,191 +1468,191 @@ size_t calc_utf8_missing( sqlsrv_stmt* stmt, const char* buffer, size_t buffer_e // The memory allocation has to happen in the core layer because otherwise // 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 ) -{ - try { +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 ) +{ + try { - close_active_stream( stmt TSRMLS_CC ); + close_active_stream( stmt TSRMLS_CC ); - // make sure that fetch is called before trying to retrieve. - CHECK_CUSTOM_ERROR( !stmt->fetch_called, stmt, SQLSRV_ERROR_FETCH_NOT_CALLED ) { - throw core::CoreException(); - } + // make sure that fetch is called before trying to retrieve. + CHECK_CUSTOM_ERROR( !stmt->fetch_called, stmt, SQLSRV_ERROR_FETCH_NOT_CALLED ) { + throw core::CoreException(); + } - // make sure that fields are not retrieved incorrectly. - CHECK_CUSTOM_ERROR( stmt->last_field_index > field_index, stmt, SQLSRV_ERROR_FIELD_INDEX_ERROR, field_index, - stmt->last_field_index ) { - throw core::CoreException(); - } + // make sure that fields are not retrieved incorrectly. + CHECK_CUSTOM_ERROR( stmt->last_field_index > field_index, stmt, SQLSRV_ERROR_FIELD_INDEX_ERROR, field_index, + stmt->last_field_index ) { + throw core::CoreException(); + } - switch( sqlsrv_php_type.typeinfo.type ) { - - case SQLSRV_PHPTYPE_INT: - { - sqlsrv_malloc_auto_ptr field_value_temp; - field_value_temp = static_cast( sqlsrv_malloc( sizeof( long ))); + switch( sqlsrv_php_type.typeinfo.type ) { - SQLRETURN r = stmt->current_results->get_data(field_index + 1, SQL_C_LONG, field_value_temp, sizeof( long ), - field_len, true /*handle_warning*/ TSRMLS_CC ); - - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw core::CoreException(); - } + case SQLSRV_PHPTYPE_INT: + { + sqlsrv_malloc_auto_ptr field_value_temp; + field_value_temp = static_cast( sqlsrv_malloc( sizeof( long ))); - CHECK_CUSTOM_ERROR( (r == SQL_NO_DATA), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { - throw core::CoreException(); - } + SQLRETURN r = stmt->current_results->get_data( field_index + 1, SQL_C_LONG, field_value_temp, sizeof( long ), + field_len, true /*handle_warning*/ TSRMLS_CC ); - if( *field_len == SQL_NULL_DATA ) { - *field_value = NULL; - break; - } - - *field_value = field_value_temp; - field_value_temp.transferred(); - break; - } + CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { + throw core::CoreException(); + } - case SQLSRV_PHPTYPE_FLOAT: - { - sqlsrv_malloc_auto_ptr field_value_temp; - field_value_temp = static_cast( sqlsrv_malloc( sizeof( double ))); + CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { + throw core::CoreException(); + } - SQLRETURN r = stmt->current_results->get_data( field_index + 1, SQL_C_DOUBLE, field_value_temp, sizeof( double ), - field_len, true /*handle_warning*/ TSRMLS_CC ); - - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw core::CoreException(); - } + if( *field_len == SQL_NULL_DATA ) { + field_value = NULL; + break; + } - CHECK_CUSTOM_ERROR( (r == SQL_NO_DATA), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { - throw core::CoreException (); - } + field_value = field_value_temp; + field_value_temp.transferred(); + break; + } - if( *field_len == SQL_NULL_DATA ) { - *field_value = NULL; - break; - } - - *field_value = field_value_temp; - field_value_temp.transferred(); - break; - } - - case SQLSRV_PHPTYPE_STRING: - { - get_field_as_string( stmt, field_index, sqlsrv_php_type, field_value, field_len TSRMLS_CC ); - break; - } - - // get the date as a string (http://msdn2.microsoft.com/en-us/library/ms712387(VS.85).aspx) and - // convert it to a DateTime object and return the created object - case SQLSRV_PHPTYPE_DATETIME: - { - char field_value_temp[ MAX_DATETIME_STRING_LEN ]; - zval params[1]; - zval field_value_temp_z; - zval function_z; - zval_auto_ptr return_value_z; - - ZVAL_UNDEF( &field_value_temp_z ); - ZVAL_UNDEF( &function_z ); - ZVAL_UNDEF( params ); - return_value_z = (zval *)sqlsrv_malloc( sizeof(zval) ); - ZVAL_UNDEF( return_value_z ); - - SQLRETURN r = stmt->current_results->get_data( field_index + 1, SQL_C_CHAR, field_value_temp, - MAX_DATETIME_STRING_LEN, field_len, true TSRMLS_CC ); + case SQLSRV_PHPTYPE_FLOAT: + { + sqlsrv_malloc_auto_ptr field_value_temp; + field_value_temp = static_cast( sqlsrv_malloc( sizeof( double ))); + + SQLRETURN r = stmt->current_results->get_data( field_index + 1, SQL_C_DOUBLE, field_value_temp, sizeof( double ), + field_len, true /*handle_warning*/ TSRMLS_CC ); - CHECK_CUSTOM_ERROR( (r == SQL_NO_DATA), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { - throw core::CoreException (); - } + CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { + throw core::CoreException(); + } - if( *field_len == SQL_NULL_DATA ) { - ZVAL_NULL( return_value_z ); - *field_value = reinterpret_cast( return_value_z.get() ); - return_value_z.transferred(); - break; - } - - // Convert the string date to a DateTime object - core::sqlsrv_zval_stringl( &field_value_temp_z, field_value_temp, *field_len ); - core::sqlsrv_zval_stringl( &function_z, "date_create", sizeof("date_create") -1 ); - params[0] = field_value_temp_z; + CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { + throw core::CoreException(); + } - if( call_user_function( EG( function_table ), NULL, &function_z, return_value_z, 1, - params TSRMLS_CC ) == FAILURE ) { - THROW_CORE_ERROR( stmt, SQLSRV_ERROR_DATETIME_CONVERSION_FAILED ); - } - - *field_value = reinterpret_cast( return_value_z.get() ); - return_value_z.transferred(); - break; - } - - // create a stream wrapper around the field and return that object to the PHP script. calls to fread - // on the stream will result in calls to SQLGetData. This is handled in stream.cpp. See that file - // for how these fields are used. - case SQLSRV_PHPTYPE_STREAM: - { - - zval_auto_ptr return_value_z; - return_value_z = (zval *)sqlsrv_malloc(sizeof(zval)); - ZVAL_UNDEF(return_value_z); - php_stream* stream = NULL; - sqlsrv_stream* ss = NULL; - SQLLEN sql_type; + if( *field_len == SQL_NULL_DATA ) { + field_value = NULL; + break; + } - SQLRETURN r = SQLColAttribute( stmt->handle(), field_index + 1, SQL_DESC_TYPE, NULL, 0, NULL, &sql_type ); - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw core::CoreException(); - } - - CHECK_CUSTOM_ERROR( !is_streamable_type( sql_type ), stmt, SQLSRV_ERROR_STREAMABLE_TYPES_ONLY ) { - throw core::CoreException(); - } - - stream = php_stream_open_wrapper( "sqlsrv://sqlncli10", "r", 0, NULL ); + field_value = field_value_temp; + field_value_temp.transferred(); + break; + } - CHECK_CUSTOM_ERROR( !stream, stmt, SQLSRV_ERROR_STREAM_CREATE ) { - throw core::CoreException(); - } + case SQLSRV_PHPTYPE_STRING: + { + get_field_as_string( stmt, field_index, sqlsrv_php_type, field_value, field_len TSRMLS_CC ); + break; + } - ss = static_cast( stream->abstract ); - ss->stmt = stmt; - ss->field_index = field_index; - ss->sql_type = static_cast( sql_type ); - ss->encoding = static_cast( sqlsrv_php_type.typeinfo.encoding ); + // get the date as a string (http://msdn2.microsoft.com/en-us/library/ms712387(VS.85).aspx) and + // convert it to a DateTime object and return the created object + case SQLSRV_PHPTYPE_DATETIME: + { + char field_value_temp[ MAX_DATETIME_STRING_LEN ]; + zval params[1]; + zval field_value_temp_z; + zval function_z; + zval_auto_ptr return_value_z; - // turn our stream into a zval to be returned - php_stream_to_zval( stream, return_value_z ); + ZVAL_UNDEF( &field_value_temp_z ); + ZVAL_UNDEF( &function_z ); + ZVAL_UNDEF( params ); + return_value_z = (zval *)sqlsrv_malloc( sizeof( zval )); + ZVAL_UNDEF( return_value_z ); - // mark this as our active stream - stmt->active_stream = return_value_z; - *field_value = reinterpret_cast( return_value_z.get() ); - return_value_z.transferred(); - break; - } + SQLRETURN r = stmt->current_results->get_data( field_index + 1, SQL_C_CHAR, field_value_temp, + MAX_DATETIME_STRING_LEN, field_len, true TSRMLS_CC ); - case SQLSRV_PHPTYPE_NULL: - *field_value = NULL; - *field_len = 0; - break; + CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { + throw core::CoreException(); + } - default: - DIE( "core_get_field_common: Unexpected sqlsrv_phptype provided" ); - break; - } + if( *field_len == SQL_NULL_DATA ) { + ZVAL_NULL( return_value_z ); + field_value = reinterpret_cast( return_value_z.get()); + return_value_z.transferred(); + break; + } - // sucessfully retrieved the field, so update our last retrieved field - if( stmt->last_field_index < field_index ) { - stmt->last_field_index = field_index; - } - } - catch( core::CoreException& e ) { - throw e; - } + // Convert the string date to a DateTime object + core::sqlsrv_zval_stringl( &field_value_temp_z, field_value_temp, *field_len ); + core::sqlsrv_zval_stringl( &function_z, "date_create", sizeof("date_create") - 1 ); + params[0] = field_value_temp_z; + + if( call_user_function( EG( function_table ), NULL, &function_z, return_value_z, 1, + params TSRMLS_CC ) == FAILURE) { + THROW_CORE_ERROR(stmt, SQLSRV_ERROR_DATETIME_CONVERSION_FAILED); + } + + field_value = reinterpret_cast( 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; + } + + // create a stream wrapper around the field and return that object to the PHP script. calls to fread + // on the stream will result in calls to SQLGetData. This is handled in stream.cpp. See that file + // for how these fields are used. + case SQLSRV_PHPTYPE_STREAM: + { + + zval_auto_ptr return_value_z; + return_value_z = (zval *)sqlsrv_malloc(sizeof(zval)); + ZVAL_UNDEF(return_value_z); + php_stream* stream = NULL; + sqlsrv_stream* ss = NULL; + SQLLEN sql_type; + + SQLRETURN r = SQLColAttribute( stmt->handle(), field_index + 1, SQL_DESC_TYPE, NULL, 0, NULL, &sql_type ); + CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { + throw core::CoreException(); + } + + CHECK_CUSTOM_ERROR( !is_streamable_type( sql_type ), stmt, SQLSRV_ERROR_STREAMABLE_TYPES_ONLY ) { + throw core::CoreException(); + } + + stream = php_stream_open_wrapper( "sqlsrv://sqlncli10", "r", 0, NULL ); + + CHECK_CUSTOM_ERROR( !stream, stmt, SQLSRV_ERROR_STREAM_CREATE ) { + throw core::CoreException(); + } + + ss = static_cast( stream->abstract ); + ss->stmt = stmt; + ss->field_index = field_index; + ss->sql_type = static_cast( sql_type ); + ss->encoding = static_cast( sqlsrv_php_type.typeinfo.encoding ); + + // turn our stream into a zval to be returned + php_stream_to_zval( stream, return_value_z ); + + field_value = reinterpret_cast( return_value_z.get()); + return_value_z.transferred(); + break; + } + + case SQLSRV_PHPTYPE_NULL: + field_value = NULL; + *field_len = 0; + break; + + default: + DIE( "core_get_field_common: Unexpected sqlsrv_phptype provided" ); + break; + } + + // sucessfully retrieved the field, so update our last retrieved field + if( stmt->last_field_index < field_index ) { + stmt->last_field_index = field_index; + } + } + catch( core::CoreException& e ) { + throw e; + } } @@ -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( output_param_temp ); zval* value_z = Z_REFVAL_P( output_param->param_z ); switch( Z_TYPE_P( value_z )) { case IS_STRING: @@ -2037,241 +2036,241 @@ 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 )); return; } -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 ) +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 ) { - SQLRETURN r; - SQLSMALLINT c_type; - SQLLEN sql_field_type = 0; - SQLSMALLINT extra = 0; - SQLLEN field_len_temp; - SQLLEN sql_display_size = 0; - char* field_value_temp = NULL; + SQLRETURN r; + SQLSMALLINT c_type; + SQLLEN sql_field_type = 0; + SQLSMALLINT extra = 0; + SQLLEN field_len_temp; + SQLLEN sql_display_size = 0; + char* field_value_temp = NULL; - try { + try { - DEBUG_SQLSRV_ASSERT( sqlsrv_php_type.typeinfo.type == SQLSRV_PHPTYPE_STRING, - "Type should be SQLSRV_PHPTYPE_STRING in get_field_as_string" ); - - if( sqlsrv_php_type.typeinfo.encoding == SQLSRV_ENCODING_DEFAULT ) { - sqlsrv_php_type.typeinfo.encoding = stmt->conn->encoding(); - } + DEBUG_SQLSRV_ASSERT( sqlsrv_php_type.typeinfo.type == SQLSRV_PHPTYPE_STRING, + "Type should be SQLSRV_PHPTYPE_STRING in get_field_as_string" ); - // Set the C type and account for null characters at the end of the data. - switch( sqlsrv_php_type.typeinfo.encoding ) { - case CP_UTF8: - c_type = SQL_C_WCHAR; - extra = sizeof( SQLWCHAR ); - break; - case SQLSRV_ENCODING_BINARY: - c_type = SQL_C_BINARY; - extra = 0; - break; - default: - c_type = SQL_C_CHAR; - extra = sizeof( SQLCHAR ); - break; - } + if( sqlsrv_php_type.typeinfo.encoding == SQLSRV_ENCODING_DEFAULT ) { + sqlsrv_php_type.typeinfo.encoding = stmt->conn->encoding(); + } - // Get the SQL type of the field. - core::SQLColAttribute( stmt, field_index + 1, SQL_DESC_CONCISE_TYPE, NULL, 0, NULL, &sql_field_type TSRMLS_CC ); + // Set the C type and account for null characters at the end of the data. + switch( sqlsrv_php_type.typeinfo.encoding ) { + case CP_UTF8: + c_type = SQL_C_WCHAR; + extra = sizeof( SQLWCHAR ); + break; + case SQLSRV_ENCODING_BINARY: + c_type = SQL_C_BINARY; + extra = 0; + break; + default: + c_type = SQL_C_CHAR; + extra = sizeof( SQLCHAR ); + break; + } - // Calculate the field size. - calc_string_size( stmt, field_index, sql_field_type, sql_display_size TSRMLS_CC ); + // Get the SQL type of the field. + core::SQLColAttribute( stmt, field_index + 1, SQL_DESC_CONCISE_TYPE, NULL, 0, NULL, &sql_field_type TSRMLS_CC ); - // if this is a large type, then read the first few bytes to get the actual length from SQLGetData - if( sql_display_size == 0 || sql_display_size == LONG_MAX || - sql_display_size == LONG_MAX >> 1 || sql_display_size == ULONG_MAX - 1 ) { + // Calculate the field size. + calc_string_size( stmt, field_index, sql_field_type, sql_display_size TSRMLS_CC ); - field_len_temp = INITIAL_FIELD_STRING_LEN; - - field_value_temp = static_cast( sqlsrv_malloc( field_len_temp + extra + 1 )); - - r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp, ( field_len_temp + extra ), - &field_len_temp, false /*handle_warning*/ TSRMLS_CC ); - - CHECK_CUSTOM_ERROR( (r == SQL_NO_DATA), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { - throw core::CoreException (); - } + // if this is a large type, then read the first few bytes to get the actual length from SQLGetData + if( sql_display_size == 0 || sql_display_size == LONG_MAX || + sql_display_size == LONG_MAX >> 1 || sql_display_size == ULONG_MAX - 1 ) { - if( field_len_temp == SQL_NULL_DATA ) { - *field_value = NULL; - sqlsrv_free( field_value_temp ); - return; - } + field_len_temp = INITIAL_FIELD_STRING_LEN; - if( r == SQL_SUCCESS_WITH_INFO ) { + field_value_temp = static_cast( sqlsrv_malloc( field_len_temp + extra + 1 )); + + r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp, ( field_len_temp + extra ), + &field_len_temp, false /*handle_warning*/ TSRMLS_CC ); + + CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { + throw core::CoreException(); + } + + if( field_len_temp == SQL_NULL_DATA ) { + field_value = NULL; + sqlsrv_free( field_value_temp ); + return; + } + + if( r == SQL_SUCCESS_WITH_INFO ) { + + SQLCHAR state[ SQL_SQLSTATE_BUFSIZE ]; + SQLSMALLINT len; + + stmt->current_results->get_diag_field( 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len TSRMLS_CC ); + + if( is_truncated_warning( state )) { - SQLCHAR state[ SQL_SQLSTATE_BUFSIZE ]; - SQLSMALLINT len; - - stmt->current_results->get_diag_field( 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len TSRMLS_CC ); - - if( is_truncated_warning( state ) ) { - SQLLEN dummy_field_len; - // for XML (and possibly other conditions) the field length returned is not the real field length, so - // in every pass, we double the allocation size to retrieve all the contents. - if( field_len_temp == SQL_NO_TOTAL ) { - - // reset the field_len_temp - field_len_temp = INITIAL_FIELD_STRING_LEN; + // for XML (and possibly other conditions) the field length returned is not the real field length, so + // in every pass, we double the allocation size to retrieve all the contents. + if( field_len_temp == SQL_NO_TOTAL ) { - do { - SQLLEN initial_field_len = field_len_temp; - // Double the size. - field_len_temp *= 2; - - field_value_temp = static_cast( sqlsrv_realloc( field_value_temp, field_len_temp + extra + 1 )); - - field_len_temp -= initial_field_len; + // reset the field_len_temp + field_len_temp = INITIAL_FIELD_STRING_LEN; - // Get the rest of the data. - r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp + initial_field_len, - field_len_temp + extra, &dummy_field_len, - false /*handle_warning*/ TSRMLS_CC ); + do { + SQLLEN initial_field_len = field_len_temp; + // Double the size. + field_len_temp *= 2; - // the last packet will contain the actual amount retrieved, not SQL_NO_TOTAL - // so we calculate the actual length of the string with that. - if( dummy_field_len != SQL_NO_TOTAL ) - field_len_temp += dummy_field_len; - else - field_len_temp += initial_field_len; + field_value_temp = static_cast( sqlsrv_realloc( field_value_temp, field_len_temp + extra + 1 )); - if( r == SQL_SUCCESS_WITH_INFO ) { - core::SQLGetDiagField( stmt, 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len - TSRMLS_CC ); - } + field_len_temp -= initial_field_len; - } while( r == SQL_SUCCESS_WITH_INFO && is_truncated_warning( state )); - } - else { + // Get the rest of the data. + r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp + initial_field_len, + field_len_temp + extra, &dummy_field_len, + false /*handle_warning*/ TSRMLS_CC ); - // We got the field_len_temp from SQLGetData call. - field_value_temp = static_cast( sqlsrv_realloc( field_value_temp, field_len_temp + extra + 1 )); - - // We have already recieved INITIAL_FIELD_STRING_LEN size data. - field_len_temp -= INITIAL_FIELD_STRING_LEN; - - // Get the rest of the data. - r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp + INITIAL_FIELD_STRING_LEN, - field_len_temp + extra, &dummy_field_len, - true /*handle_warning*/ TSRMLS_CC ); + // the last packet will contain the actual amount retrieved, not SQL_NO_TOTAL + // so we calculate the actual length of the string with that. + if( dummy_field_len != SQL_NO_TOTAL ) + field_len_temp += dummy_field_len; + else + field_len_temp += initial_field_len; - if( dummy_field_len == SQL_NULL_DATA ) { - *field_value = NULL; - sqlsrv_free( field_value_temp ); - return; - } - - CHECK_CUSTOM_ERROR( (r == SQL_NO_DATA), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { - throw core::CoreException (); - } - - field_len_temp += INITIAL_FIELD_STRING_LEN; - } + if( r == SQL_SUCCESS_WITH_INFO ) { + core::SQLGetDiagField( stmt, 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len + TSRMLS_CC ); + } - } // if( is_truncation_warning ( state ) ) - else { - CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { - throw core::CoreException(); - } - } - } // if( r == SQL_SUCCESS_WITH_INFO ) + } while( r == SQL_SUCCESS_WITH_INFO && is_truncated_warning( state )); + } + else { - if( sqlsrv_php_type.typeinfo.encoding == SQLSRV_ENCODING_UTF8 ) { + // We got the field_len_temp from SQLGetData call. + field_value_temp = static_cast( sqlsrv_realloc( field_value_temp, field_len_temp + extra + 1 )); - bool converted = convert_string_from_utf16_inplace( static_cast( sqlsrv_php_type.typeinfo.encoding ), - &field_value_temp, field_len_temp ); - - CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, get_last_error_message() ) { - throw core::CoreException (); - } - } - } // if ( sql_display_size == 0 || sql_display_size == LONG_MAX .. ) - - else if( sql_display_size >= 1 && sql_display_size <= SQL_SERVER_MAX_FIELD_SIZE ) { + // We have already recieved INITIAL_FIELD_STRING_LEN size data. + field_len_temp -= INITIAL_FIELD_STRING_LEN; - // only allow binary retrievals for char and binary types. All others get a string converted - // to the encoding type they asked for. - - // null terminator - if( c_type == SQL_C_CHAR ) { - sql_display_size += sizeof( SQLCHAR ); - } + // Get the rest of the data. + r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp + INITIAL_FIELD_STRING_LEN, + field_len_temp + extra, &dummy_field_len, + true /*handle_warning*/ TSRMLS_CC ); - // For WCHAR multiply by sizeof(WCHAR) and include the null terminator - else if( c_type == SQL_C_WCHAR ) { - sql_display_size = (sql_display_size * sizeof(WCHAR)) + sizeof(WCHAR); - } + if( dummy_field_len == SQL_NULL_DATA ) { + field_value = NULL; + sqlsrv_free( field_value_temp ); + return; + } - field_value_temp = static_cast( sqlsrv_malloc( sql_display_size + extra + 1 )); - - // get the data - r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp, sql_display_size, - &field_len_temp, true /*handle_warning*/ TSRMLS_CC ); - CHECK_SQL_ERROR( r, stmt ) { - throw core::CoreException(); - } - CHECK_CUSTOM_ERROR( (r == SQL_NO_DATA), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { - throw core::CoreException (); - } + CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { + throw core::CoreException(); + } - if( field_len_temp == SQL_NULL_DATA ) { - *field_value = NULL; - sqlsrv_free( field_value_temp ); - return; - } - - if( sqlsrv_php_type.typeinfo.encoding == CP_UTF8 ) { + field_len_temp += INITIAL_FIELD_STRING_LEN; + } - bool converted = convert_string_from_utf16_inplace( static_cast( sqlsrv_php_type.typeinfo.encoding ), - &field_value_temp, field_len_temp ); - - CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, get_last_error_message() ) { - throw core::CoreException (); - } - } - } // else if( sql_display_size >= 1 && sql_display_size <= SQL_SERVER_MAX_FIELD_SIZE ) + } // if( is_truncation_warning ( state ) ) + else { + CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { + throw core::CoreException(); + } + } + } // if( r == SQL_SUCCESS_WITH_INFO ) - else { - - DIE( "Invalid sql_display_size" ); - return; // to eliminate a warning - } - - *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 - // runtime checks to see if a string is null terminated and issues a warning about it if running in debug mode. - // SQL_C_BINARY fields don't return a NULL terminator, so we allocate an extra byte on each field and use the ternary - // operator to set add 1 to fill the null terminator - field_value_temp[field_len_temp] = '\0'; - } + if( sqlsrv_php_type.typeinfo.encoding == SQLSRV_ENCODING_UTF8 ) { - catch( core::CoreException& ) { + bool converted = convert_string_from_utf16_inplace( static_cast( sqlsrv_php_type.typeinfo.encoding ), + &field_value_temp, field_len_temp ); - *field_value = NULL; - *field_len = 0; - sqlsrv_free( field_value_temp ); - throw; - } - catch ( ... ) { + CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, get_last_error_message()) { + throw core::CoreException(); + } + } + } // if ( sql_display_size == 0 || sql_display_size == LONG_MAX .. ) - *field_value = NULL; - *field_len = 0; - sqlsrv_free( field_value_temp ); - throw; - } + else if( sql_display_size >= 1 && sql_display_size <= SQL_SERVER_MAX_FIELD_SIZE ) { + + // only allow binary retrievals for char and binary types. All others get a string converted + // to the encoding type they asked for. + + // null terminator + if( c_type == SQL_C_CHAR ) { + sql_display_size += sizeof( SQLCHAR ); + } + + // For WCHAR multiply by sizeof(WCHAR) and include the null terminator + else if( c_type == SQL_C_WCHAR ) { + sql_display_size = (sql_display_size * sizeof(WCHAR)) + sizeof(WCHAR); + } + + field_value_temp = static_cast( sqlsrv_malloc( sql_display_size + extra + 1 )); + + // get the data + r = stmt->current_results->get_data( field_index + 1, c_type, field_value_temp, sql_display_size, + &field_len_temp, true /*handle_warning*/ TSRMLS_CC ); + CHECK_SQL_ERROR( r, stmt ) { + throw core::CoreException(); + } + CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) { + throw core::CoreException(); + } + + if( field_len_temp == SQL_NULL_DATA ) { + field_value = NULL; + sqlsrv_free( field_value_temp ); + return; + } + + if( sqlsrv_php_type.typeinfo.encoding == CP_UTF8 ) { + + bool converted = convert_string_from_utf16_inplace( static_cast( sqlsrv_php_type.typeinfo.encoding ), + &field_value_temp, field_len_temp ); + + CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE, get_last_error_message()) { + throw core::CoreException(); + } + } + } // else if( sql_display_size >= 1 && sql_display_size <= SQL_SERVER_MAX_FIELD_SIZE ) + + else { + + DIE( "Invalid sql_display_size" ); + return; // to eliminate a warning + } + + 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 + // runtime checks to see if a string is null terminated and issues a warning about it if running in debug mode. + // SQL_C_BINARY fields don't return a NULL terminator, so we allocate an extra byte on each field and use the ternary + // operator to set add 1 to fill the null terminator + field_value_temp[field_len_temp] = '\0'; + } + + catch( core::CoreException& ) { + + field_value = NULL; + *field_len = 0; + sqlsrv_free( field_value_temp ); + throw; + } + catch ( ... ) { + + field_value = NULL; + *field_len = 0; + sqlsrv_free( field_value_temp ); + throw; + } } diff --git a/sqlsrv/core_stream.cpp b/sqlsrv/core_stream.cpp index 17468d8c..a709c186 100644 --- a/sqlsrv/core_stream.cpp +++ b/sqlsrv/core_stream.cpp @@ -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; diff --git a/sqlsrv/init.cpp b/sqlsrv/init.cpp index e66db0d1..e05fa856 100644 --- a/sqlsrv/init.cpp +++ b/sqlsrv/init.cpp @@ -493,17 +493,17 @@ PHP_MINIT_FUNCTION(sqlsrv) zend_hash_init( g_ss_encodings_ht, 3, NULL /*use standard hash function*/, sqlsrv_encoding_dtor /*resource destructor*/, 1 /*persistent*/ ); sqlsrv_encoding sql_enc_char( "char", SQLSRV_ENCODING_CHAR ); - if (NULL == zend_hash_next_index_insert_mem(g_ss_encodings_ht, (void*)&sql_enc_char, sizeof( sqlsrv_encoding ))) { + if (NULL == zend_hash_next_index_insert_mem( g_ss_encodings_ht, (void*)&sql_enc_char, sizeof( sqlsrv_encoding ))) { throw ss::SSException(); } sqlsrv_encoding sql_enc_bin( "binary", SQLSRV_ENCODING_BINARY, true ); - if (NULL == zend_hash_next_index_insert_mem(g_ss_encodings_ht, (void*)&sql_enc_bin, sizeof( sqlsrv_encoding ))) { + if (NULL == zend_hash_next_index_insert_mem( g_ss_encodings_ht, (void*)&sql_enc_bin, sizeof( sqlsrv_encoding ))) { throw ss::SSException(); } sqlsrv_encoding sql_enc_utf8( "utf-8", CP_UTF8 ); - if (NULL == zend_hash_next_index_insert_mem(g_ss_encodings_ht, (void*)&sql_enc_utf8, sizeof( sqlsrv_encoding ))) { + if (NULL == zend_hash_next_index_insert_mem( g_ss_encodings_ht, (void*)&sql_enc_utf8, sizeof( sqlsrv_encoding ))) { throw ss::SSException(); } } @@ -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( Z_PTR_P(elem) ); + sqlsrv_error_const* error_to_ignore = static_cast( 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( Z_PTR_P(elem) ); + sqlsrv_encoding* sql_enc = static_cast( 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; } diff --git a/sqlsrv/msodbcsql.h b/sqlsrv/msodbcsql.h index a16bf59a..e1678e5c 100644 --- a/sqlsrv/msodbcsql.h +++ b/sqlsrv/msodbcsql.h @@ -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) @@ -165,12 +167,12 @@ extern "C" { #endif -// max SQL Server identifier length + // max SQL Server identifier length #define SQL_MAX_SQLSERVERNAME 128 -// SQLSetConnectAttr driver specific defines. -// Microsoft has 1200 thru 1249 reserved for Microsoft SQL Server Native Client driver usage. -// Connection attributes + // SQLSetConnectAttr driver specific defines. + // Microsoft has 1200 thru 1249 reserved for Microsoft SQL Server Native Client driver usage. + // Connection attributes #define SQL_COPT_SS_BASE 1200 #define SQL_COPT_SS_REMOTE_PWD (SQL_COPT_SS_BASE+1) // dbrpwset SQLSetConnectOption only #define SQL_COPT_SS_USE_PROC_FOR_PREP (SQL_COPT_SS_BASE+2) // Use create proc for SQLPrepare @@ -203,14 +205,18 @@ 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 old names + +#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 #define SQL_INTEGRATED_SECURITY SQL_COPT_SS_INTEGRATED_SECURITY #define SQL_PRESERVE_CURSORS SQL_COPT_SS_PRESERVE_CURSORS -// SQLSetStmtAttr SQL Server Native Client driver specific defines. -// Statement attributes + // SQLSetStmtAttr SQL Server Native Client driver specific defines. + // Statement attributes #define SQL_SOPT_SS_BASE 1225 #define SQL_SOPT_SS_TEXTPTR_LOGGING (SQL_SOPT_SS_BASE+0) // Text pointer logging #define SQL_SOPT_SS_CURRENT_COMMAND (SQL_SOPT_SS_BASE+1) // dbcurcmd SQLGetStmtOption only @@ -225,8 +231,9 @@ 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 old names +#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 #define SQL_COPT_SS_BROWSE_CONNECT (SQL_COPT_SS_BASE_EX+1) // Browse connect mode of operation @@ -237,11 +244,17 @@ 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. -// Microsoft has 1200 thru 1249 reserved for Microsoft SQL Server Native Client driver usage. + // SQLColAttributes driver specific defines. + // SQLSetDescField/SQLGetDescField driver specific defines. + // Microsoft has 1200 thru 1249 reserved for Microsoft SQL Server Native Client driver usage. #define SQL_CA_SS_BASE 1200 #define SQL_CA_SS_COLUMN_SSTYPE (SQL_CA_SS_BASE+0) // dbcoltype/dbalttype #define SQL_CA_SS_COLUMN_UTYPE (SQL_CA_SS_BASE+1) // dbcolutype/dbaltutype @@ -256,13 +269,13 @@ extern "C" { #define SQL_CA_SS_COLUMN_SIZE (SQL_CA_SS_BASE+10) // dbcollen #define SQL_CA_SS_COLUMN_HIDDEN (SQL_CA_SS_BASE+11) // Column is hidden (FOR BROWSE) #define SQL_CA_SS_COLUMN_KEY (SQL_CA_SS_BASE+12) // Column is key column (FOR BROWSE) -//#define SQL_DESC_BASE_COLUMN_NAME_OLD (SQL_CA_SS_BASE+13) // This is defined at another location. + //#define SQL_DESC_BASE_COLUMN_NAME_OLD (SQL_CA_SS_BASE+13) // This is defined at another location. #define SQL_CA_SS_COLUMN_COLLATION (SQL_CA_SS_BASE+14) // Column collation (only for chars) #define SQL_CA_SS_VARIANT_TYPE (SQL_CA_SS_BASE+15) #define SQL_CA_SS_VARIANT_SQL_TYPE (SQL_CA_SS_BASE+16) #define SQL_CA_SS_VARIANT_SERVER_TYPE (SQL_CA_SS_BASE+17) -// XML, CLR UDT, and table valued parameter related metadata + // XML, CLR UDT, and table valued parameter related metadata #define SQL_CA_SS_UDT_CATALOG_NAME (SQL_CA_SS_BASE+18) // UDT catalog name #define SQL_CA_SS_UDT_SCHEMA_NAME (SQL_CA_SS_BASE+19) // UDT schema name #define SQL_CA_SS_UDT_TYPE_NAME (SQL_CA_SS_BASE+20) // UDT type name @@ -274,128 +287,143 @@ extern "C" { #define SQL_CA_SS_SCHEMA_NAME (SQL_CA_SS_BASE+26) // Schema name #define SQL_CA_SS_TYPE_NAME (SQL_CA_SS_BASE+27) // Type name -// table valued parameter related metadata + // table valued parameter related metadata #define SQL_CA_SS_COLUMN_COMPUTED (SQL_CA_SS_BASE+29) // column is computed #define SQL_CA_SS_COLUMN_IN_UNIQUE_KEY (SQL_CA_SS_BASE+30) // column is part of a unique key #define SQL_CA_SS_COLUMN_SORT_ORDER (SQL_CA_SS_BASE+31) // column sort order #define SQL_CA_SS_COLUMN_SORT_ORDINAL (SQL_CA_SS_BASE+32) // column sort ordinal #define SQL_CA_SS_COLUMN_HAS_DEFAULT_VALUE (SQL_CA_SS_BASE+33) // column has default value for all rows of the table valued parameter -// sparse column related metadata + // sparse column related metadata #define SQL_CA_SS_IS_COLUMN_SET (SQL_CA_SS_BASE+34) // column is a column-set column for sparse columns -// Legacy datetime related metadata + // 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 -// Defines returned by SQL_ATTR_CURSOR_TYPE/SQL_CURSOR_TYPE +#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 -// Defines for use with SQL_COPT_SS_USE_PROC_FOR_PREP + // Defines for use with SQL_COPT_SS_USE_PROC_FOR_PREP #define SQL_UP_OFF 0L // Procedures won't be used for prepare #define SQL_UP_ON 1L // Procedures will be used for prepare #define SQL_UP_ON_DROP 2L // Temp procedures will be explicitly dropped #define SQL_UP_DEFAULT SQL_UP_ON -// Defines for use with SQL_COPT_SS_INTEGRATED_SECURITY - Pre-Connect Option only + // 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 + // Defines for use with SQL_COPT_SS_PRESERVE_CURSORS #define SQL_PC_OFF 0L // Cursors are closed on SQLTransact #define SQL_PC_ON 1L // Cursors remain open on SQLTransact #define SQL_PC_DEFAULT SQL_PC_OFF -// Defines for use with SQL_COPT_SS_USER_DATA + // Defines for use with SQL_COPT_SS_USER_DATA #define SQL_UD_NOTSET NULL // No user data pointer set -// Defines for use with SQL_COPT_SS_TRANSLATE + // Defines for use with SQL_COPT_SS_TRANSLATE #define SQL_XL_OFF 0L // Code page translation is not performed #define SQL_XL_ON 1L // Code page translation is performed #define SQL_XL_DEFAULT SQL_XL_ON -// Defines for use with SQL_COPT_SS_FALLBACK_CONNECT - Pre-Connect Option only + // Defines for use with SQL_COPT_SS_FALLBACK_CONNECT - Pre-Connect Option only #define SQL_FB_OFF 0L // FallBack connections are disabled #define SQL_FB_ON 1L // FallBack connections are enabled #define SQL_FB_DEFAULT SQL_FB_OFF -// Defines for use with SQL_COPT_SS_BCP - Pre-Connect Option only + // Defines for use with SQL_COPT_SS_BCP - Pre-Connect Option only #define SQL_BCP_OFF 0L // BCP is not allowed on connection #define SQL_BCP_ON 1L // BCP is allowed on connection #define SQL_BCP_DEFAULT SQL_BCP_OFF -// Defines for use with SQL_COPT_SS_QUOTED_IDENT + // Defines for use with SQL_COPT_SS_QUOTED_IDENT #define SQL_QI_OFF 0L // Quoted identifiers are enable #define SQL_QI_ON 1L // Quoted identifiers are disabled #define SQL_QI_DEFAULT SQL_QI_ON -// Defines for use with SQL_COPT_SS_ANSI_NPW - Pre-Connect Option only + // Defines for use with SQL_COPT_SS_ANSI_NPW - Pre-Connect Option only #define SQL_AD_OFF 0L // ANSI NULLs, Padding and Warnings are enabled #define SQL_AD_ON 1L // ANSI NULLs, Padding and Warnings are disabled #define SQL_AD_DEFAULT SQL_AD_ON -// Defines for use with SQL_COPT_SS_CONCAT_NULL - Pre-Connect Option only + // Defines for use with SQL_COPT_SS_CONCAT_NULL - Pre-Connect Option only #define SQL_CN_OFF 0L // CONCAT_NULL_YIELDS_NULL is off #define SQL_CN_ON 1L // CONCAT_NULL_YIELDS_NULL is on #define SQL_CN_DEFAULT SQL_CN_ON -// Defines for use with SQL_SOPT_SS_TEXTPTR_LOGGING + // Defines for use with SQL_SOPT_SS_TEXTPTR_LOGGING #define SQL_TL_OFF 0L // No logging on text pointer ops #define SQL_TL_ON 1L // Logging occurs on text pointer ops #define SQL_TL_DEFAULT SQL_TL_ON -// Defines for use with SQL_SOPT_SS_HIDDEN_COLUMNS + // Defines for use with SQL_SOPT_SS_HIDDEN_COLUMNS #define SQL_HC_OFF 0L // FOR BROWSE columns are hidden #define SQL_HC_ON 1L // FOR BROWSE columns are exposed #define SQL_HC_DEFAULT SQL_HC_OFF -// Defines for use with SQL_SOPT_SS_NOBROWSETABLE + // Defines for use with SQL_SOPT_SS_NOBROWSETABLE #define SQL_NB_OFF 0L // NO_BROWSETABLE is off #define SQL_NB_ON 1L // NO_BROWSETABLE is on #define SQL_NB_DEFAULT SQL_NB_OFF -// Defines for use with SQL_SOPT_SS_REGIONALIZE + // Defines for use with SQL_SOPT_SS_REGIONALIZE #define SQL_RE_OFF 0L // No regionalization occurs on output character conversions #define SQL_RE_ON 1L // Regionalization occurs on output character conversions #define SQL_RE_DEFAULT SQL_RE_OFF -// Defines for use with SQL_SOPT_SS_CURSOR_OPTIONS + // Defines for use with SQL_SOPT_SS_CURSOR_OPTIONS #define SQL_CO_OFF 0L // Clear all cursor options #define SQL_CO_FFO 1L // Fast-forward cursor will be used #define SQL_CO_AF 2L // Autofetch on cursor open #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 -//SQL_SOPT_SS_NOCOUNT_STATUS + // 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 -//SQL_SOPT_SS_DEFER_PREPARE + //SQL_SOPT_SS_DEFER_PREPARE #define SQL_DP_OFF 0L #define SQL_DP_ON 1L -//SQL_SOPT_SS_NAME_SCOPE + //SQL_SOPT_SS_NAME_SCOPE #define SQL_SS_NAME_SCOPE_TABLE 0L #define SQL_SS_NAME_SCOPE_TABLE_TYPE 1L #define SQL_SS_NAME_SCOPE_EXTENDED 2L #define SQL_SS_NAME_SCOPE_SPARSE_COLUMN_SET 3L #define SQL_SS_NAME_SCOPE_DEFAULT SQL_SS_NAME_SCOPE_TABLE -//SQL_COPT_SS_ENCRYPT + //SQL_COPT_SS_ENCRYPT #define SQL_EN_OFF 0L #define SQL_EN_ON 1L -//SQL_COPT_SS_TRUST_SERVER_CERTIFICATE + //SQL_COPT_SS_TRUST_SERVER_CERTIFICATE #define SQL_TRUST_SERVER_CERTIFICATE_NO 0L #define SQL_TRUST_SERVER_CERTIFICATE_YES 1L -//SQL_COPT_SS_BROWSE_CONNECT + //SQL_COPT_SS_BROWSE_CONNECT #define SQL_MORE_INFO_NO 0L #define SQL_MORE_INFO_YES 1L -//SQL_COPT_SS_BROWSE_CACHE_DATA + //SQL_COPT_SS_BROWSE_CACHE_DATA #define SQL_CACHE_DATA_NO 0L #define SQL_CACHE_DATA_YES 1L -//SQL_COPT_SS_RESET_CONNECTION + //SQL_COPT_SS_RESET_CONNECTION #define SQL_RESET_YES 1L -//SQL_COPT_SS_WARN_ON_CP_ERROR + //SQL_COPT_SS_WARN_ON_CP_ERROR #define SQL_WARN_NO 0L #define SQL_WARN_YES 1L -//SQL_COPT_SS_MARS_ENABLED + //SQL_COPT_SS_MARS_ENABLED #define SQL_MARS_ENABLED_NO 0L #define SQL_MARS_ENABLED_YES 1L -/* SQL_TXN_ISOLATION_OPTION bitmasks */ + /* SQL_TXN_ISOLATION_OPTION bitmasks */ #define SQL_TXN_SS_SNAPSHOT 0x00000020L -// The following are defines for SQL_CA_SS_COLUMN_SORT_ORDER + // The following are defines for SQL_CA_SS_COLUMN_SORT_ORDER #define SQL_SS_ORDER_UNSPECIFIED 0L #define SQL_SS_DESCENDING_ORDER 1L #define SQL_SS_ASCENDING_ORDER 2L #define SQL_SS_ORDER_DEFAULT SQL_SS_ORDER_UNSPECIFIED -// Driver specific SQL data type defines. -// Microsoft has -150 thru -199 reserved for Microsoft SQL Server Native Client driver usage. + // Driver specific SQL data type defines. + // Microsoft has -150 thru -199 reserved for Microsoft SQL Server Native Client driver usage. #define SQL_SS_VARIANT (-150) #define SQL_SS_UDT (-151) #define SQL_SS_XML (-152) @@ -403,22 +431,22 @@ extern "C" { #define SQL_SS_TIME2 (-154) #define SQL_SS_TIMESTAMPOFFSET (-155) -// Local types to be used with SQL_CA_SS_SERVER_TYPE + // Local types to be used with SQL_CA_SS_SERVER_TYPE #define SQL_SS_TYPE_DEFAULT 0L #define SQL_SS_TYPE_SMALLDATETIME 1L #define SQL_SS_TYPE_DATETIME 2L -// Extended C Types range 4000 and above. Range of -100 thru 200 is reserved by Driver Manager. + // Extended C Types range 4000 and above. Range of -100 thru 200 is reserved by Driver Manager. #define SQL_C_TYPES_EXTENDED 0x04000L #define SQL_C_SS_TIME2 (SQL_C_TYPES_EXTENDED+0) #define SQL_C_SS_TIMESTAMPOFFSET (SQL_C_TYPES_EXTENDED+1) #ifndef SQLNCLI_NO_BCP -// Define the symbol SQLNCLI_NO_BCP if you are not using BCP in your application -// and you want to exclude the BCP-related definitions in this header file. + // Define the symbol SQLNCLI_NO_BCP if you are not using BCP in your application + // and you want to exclude the BCP-related definitions in this header file. -// SQL Server Data Type defines. -// New types for SQL 6.0 and later servers + // SQL Server Data Type defines. + // New types for SQL 6.0 and later servers #define SQLTEXT 0x23 #define SQLVARBINARY 0x25 #define SQLINTN 0x26 @@ -439,10 +467,10 @@ extern "C" { #define SQLFLT4 0x3b #define SQLMONEY4 0x7a #define SQLDATETIM4 0x3a -// New types for SQL 6.0 and later servers + // New types for SQL 6.0 and later servers #define SQLDECIMAL 0x6a #define SQLNUMERIC 0x6c -// New types for SQL 7.0 and later servers + // New types for SQL 7.0 and later servers #define SQLUNIQUEID 0x24 #define SQLBIGCHAR 0xaf #define SQLBIGVARCHAR 0xa7 @@ -452,29 +480,29 @@ extern "C" { #define SQLNCHAR 0xef #define SQLNVARCHAR 0xe7 #define SQLNTEXT 0x63 -// New types for SQL 2000 and later servers + // New types for SQL 2000 and later servers #define SQLINT8 0x7f #define SQLVARIANT 0x62 -// New types for SQL 2005 and later servers + // New types for SQL 2005 and later servers #define SQLUDT 0xf0 #define SQLXML 0xf1 -// New types for SQL 2008 and later servers + // New types for SQL 2008 and later servers #define SQLTABLE 0xf3 #define SQLDATEN 0x28 #define SQLTIMEN 0x29 #define SQLDATETIME2N 0x2a #define SQLDATETIMEOFFSETN 0x2b -// Define old names + // Define old names #define SQLDECIMALN 0x6a #define SQLNUMERICN 0x6c #endif // SQLNCLI_NO_BCP -// SQL_SS_LENGTH_UNLIMITED is used to describe the max length of -// VARCHAR(max), VARBINARY(max), NVARCHAR(max), and XML columns + // SQL_SS_LENGTH_UNLIMITED is used to describe the max length of + // VARCHAR(max), VARBINARY(max), NVARCHAR(max), and XML columns #define SQL_SS_LENGTH_UNLIMITED 0 -// User Data Type definitions. -// Returned by SQLColAttributes/SQL_CA_SS_COLUMN_UTYPE. + // User Data Type definitions. + // Returned by SQLColAttributes/SQL_CA_SS_COLUMN_UTYPE. #define SQLudtBINARY 3 #define SQLudtBIT 16 #define SQLudtBITN 0 @@ -504,8 +532,8 @@ extern "C" { #define SQLudtVARBINARY 4 #define SQLudtVARCHAR 2 #define MIN_USER_DATATYPE 256 -// Aggregate operator types. -// Returned by SQLColAttributes/SQL_CA_SS_COLUMN_OP. + // Aggregate operator types. + // Returned by SQLColAttributes/SQL_CA_SS_COLUMN_OP. #define SQLAOPSTDEV 0x30 // Standard deviation #define SQLAOPSTDEVP 0x31 // Standard deviation population #define SQLAOPVAR 0x32 // Variance @@ -517,8 +545,8 @@ extern "C" { #define SQLAOPMAX 0x52 // Max #define SQLAOPANY 0x53 // Any #define SQLAOPNOOP 0x56 // None -// SQLGetInfo driver specific defines. -// Microsoft has 1151 thru 1200 reserved for Microsoft SQL Server Native Client driver usage. + // SQLGetInfo driver specific defines. + // Microsoft has 1151 thru 1200 reserved for Microsoft SQL Server Native Client driver usage. #define SQL_INFO_SS_FIRST 1199 #define SQL_INFO_SS_NETLIB_NAMEW (SQL_INFO_SS_FIRST+0) // dbprocinfo #define SQL_INFO_SS_NETLIB_NAMEA (SQL_INFO_SS_FIRST+1) // dbprocinfo @@ -529,16 +557,16 @@ extern "C" { #define SQL_INFO_SS_NETLIB_NAME SQL_INFO_SS_NETLIB_NAMEA #endif -// SQLGetDiagField driver specific defines. -// Microsoft has -1150 thru -1199 reserved for Microsoft SQL Server Native Client driver usage. + // SQLGetDiagField driver specific defines. + // Microsoft has -1150 thru -1199 reserved for Microsoft SQL Server Native Client driver usage. #define SQL_DIAG_SS_BASE (-1150) #define SQL_DIAG_SS_MSGSTATE (SQL_DIAG_SS_BASE) #define SQL_DIAG_SS_SEVERITY (SQL_DIAG_SS_BASE-1) #define SQL_DIAG_SS_SRVNAME (SQL_DIAG_SS_BASE-2) #define SQL_DIAG_SS_PROCNAME (SQL_DIAG_SS_BASE-3) #define SQL_DIAG_SS_LINE (SQL_DIAG_SS_BASE-4) -// SQLGetDiagField/SQL_DIAG_DYNAMIC_FUNCTION_CODE driver specific defines. -// Microsoft has -200 thru -299 reserved for Microsoft SQL Server Native Client driver usage. + // SQLGetDiagField/SQL_DIAG_DYNAMIC_FUNCTION_CODE driver specific defines. + // Microsoft has -200 thru -299 reserved for Microsoft SQL Server Native Client driver usage. #define SQL_DIAG_DFC_SS_BASE (-200) #define SQL_DIAG_DFC_SS_ALTER_DATABASE (SQL_DIAG_DFC_SS_BASE-0) #define SQL_DIAG_DFC_SS_CHECKPOINT (SQL_DIAG_DFC_SS_BASE-1) @@ -603,7 +631,7 @@ extern "C" { #define SQL_DIAG_DFC_SS_SET_XCTLVL (SQL_DIAG_DFC_SS_BASE-55) #define SQL_DIAG_DFC_SS_MERGE (SQL_DIAG_DFC_SS_BASE-56) -// Severity codes for SQL_DIAG_SS_SEVERITY + // Severity codes for SQL_DIAG_SS_SEVERITY #define EX_ANY 0 #define EX_INFO 10 #define EX_MAXISEVERITY EX_INFO @@ -624,75 +652,75 @@ extern "C" { #define EX_DBCORRUPT 23 #define EX_HARDWARE 24 #define EX_CONTROL 25 -// Internal server datatypes - used when binding to SQL_C_BINARY + // Internal server datatypes - used when binding to SQL_C_BINARY #ifndef MAXNUMERICLEN // Resolve ODS/DBLib conflicts -// DB-Library datatypes + // DB-Library datatypes #define DBMAXCHAR (8000+1) // Max length of DBVARBINARY and DBVARCHAR, etc. +1 for zero byte #define MAXNAME (SQL_MAX_SQLSERVERNAME+1) // Max server identifier length including zero byte #ifdef UNICODE -typedef wchar_t DBCHAR; + typedef wchar_t DBCHAR; #else -typedef char DBCHAR; + typedef char DBCHAR; #endif -typedef short SQLSMALLINT; + typedef short SQLSMALLINT; -typedef unsigned short SQLUSMALLINT; + typedef unsigned short SQLUSMALLINT; -typedef unsigned char DBBINARY; + typedef unsigned char DBBINARY; -typedef unsigned char DBTINYINT; + typedef unsigned char DBTINYINT; -typedef short DBSMALLINT; + typedef short DBSMALLINT; -typedef unsigned short DBUSMALLINT; + typedef unsigned short DBUSMALLINT; -typedef double DBFLT8; + typedef double DBFLT8; -typedef unsigned char DBBIT; + typedef unsigned char DBBIT; -typedef unsigned char DBBOOL; + typedef unsigned char DBBOOL; -typedef float DBFLT4; + typedef float DBFLT4; -typedef DBFLT4 DBREAL; + typedef DBFLT4 DBREAL; -typedef UINT DBUBOOL; + typedef UINT DBUBOOL; -typedef struct dbmoney + typedef struct dbmoney { - LONG mnyhigh; - ULONG mnylow; + LONG mnyhigh; + ULONG mnylow; } DBMONEY; -typedef struct dbdatetime + typedef struct dbdatetime { - LONG dtdays; - ULONG dttime; + LONG dtdays; + ULONG dttime; } DBDATETIME; -typedef struct dbdatetime4 + typedef struct dbdatetime4 { - USHORT numdays; - USHORT nummins; + USHORT numdays; + USHORT nummins; } DBDATETIM4; -typedef LONG DBMONEY4; + typedef LONG DBMONEY4; #include // 8-byte structure packing -// New Date Time Structures -// New Structure for TIME2 -typedef struct tagSS_TIME2_STRUCT -{ + // New Date Time Structures + // New Structure for TIME2 + typedef struct tagSS_TIME2_STRUCT + { SQLUSMALLINT hour; SQLUSMALLINT minute; SQLUSMALLINT second; SQLUINTEGER fraction; -} SQL_SS_TIME2_STRUCT; -// New Structure for TIMESTAMPOFFSET -typedef struct tagSS_TIMESTAMPOFFSET_STRUCT -{ + } SQL_SS_TIME2_STRUCT; + // New Structure for TIMESTAMPOFFSET + typedef struct tagSS_TIMESTAMPOFFSET_STRUCT + { SQLSMALLINT year; SQLUSMALLINT month; SQLUSMALLINT day; @@ -702,130 +730,130 @@ typedef struct tagSS_TIMESTAMPOFFSET_STRUCT SQLUINTEGER fraction; SQLSMALLINT timezone_hour; SQLSMALLINT timezone_minute; -} SQL_SS_TIMESTAMPOFFSET_STRUCT; + } SQL_SS_TIMESTAMPOFFSET_STRUCT; -typedef struct tagDBTIME2 -{ - USHORT hour; - USHORT minute; - USHORT second; - ULONG fraction; -} DBTIME2; + typedef struct tagDBTIME2 + { + USHORT hour; + USHORT minute; + USHORT second; + ULONG fraction; + } DBTIME2; -typedef struct tagDBTIMESTAMPOFFSET -{ - SHORT year; - USHORT month; - USHORT day; - USHORT hour; - USHORT minute; - USHORT second; - ULONG fraction; - SHORT timezone_hour; - SHORT timezone_minute; -} DBTIMESTAMPOFFSET; + typedef struct tagDBTIMESTAMPOFFSET + { + SHORT year; + USHORT month; + USHORT day; + USHORT hour; + USHORT minute; + USHORT second; + ULONG fraction; + SHORT timezone_hour; + SHORT timezone_minute; + } DBTIMESTAMPOFFSET; #include // restore original structure packing -// Money value *10,000 + // Money value *10,000 #define DBNUM_PREC_TYPE BYTE #define DBNUM_SCALE_TYPE BYTE #define DBNUM_VAL_TYPE BYTE #if (ODBCVER < 0x0300) #define MAXNUMERICLEN 16 -typedef struct dbnumeric // Internal representation of NUMERIC data type -{ - DBNUM_PREC_TYPE precision; // Precision - DBNUM_SCALE_TYPE scale; // Scale - BYTE sign; // Sign (1 if positive, 0 if negative) - DBNUM_VAL_TYPE val[MAXNUMERICLEN];// Value -} DBNUMERIC; -typedef DBNUMERIC DBDECIMAL;// Internal representation of DECIMAL data type + typedef struct dbnumeric // Internal representation of NUMERIC data type + { + DBNUM_PREC_TYPE precision; // Precision + DBNUM_SCALE_TYPE scale; // Scale + BYTE sign; // Sign (1 if positive, 0 if negative) + DBNUM_VAL_TYPE val[MAXNUMERICLEN];// Value + } DBNUMERIC; + typedef DBNUMERIC DBDECIMAL;// Internal representation of DECIMAL data type #else // Use ODBC 3.0 definitions since same as DBLib #define MAXNUMERICLEN SQL_MAX_NUMERIC_LEN -typedef SQL_NUMERIC_STRUCT DBNUMERIC; -typedef SQL_NUMERIC_STRUCT DBDECIMAL; + typedef SQL_NUMERIC_STRUCT DBNUMERIC; + typedef SQL_NUMERIC_STRUCT DBDECIMAL; #endif // ODCBVER #endif // MAXNUMERICLEN #ifndef INT -typedef int INT; -typedef LONG DBINT; -typedef DBINT * LPDBINT; + typedef int INT; + typedef LONG DBINT; + typedef DBINT * LPDBINT; #ifndef _LPCBYTE_DEFINED #define _LPCBYTE_DEFINED -typedef BYTE const* LPCBYTE; + typedef BYTE const* LPCBYTE; #endif //_LPCBYTE_DEFINED #endif // INT -/************************************************************************** -This struct is a global used for gathering statistical data on the driver. -Access to this structure is controlled via the pStatCrit; -***************************************************************************/ -typedef struct sqlperf -{ - // Application Profile Statistics - DWORD TimerResolution; - DWORD SQLidu; - DWORD SQLiduRows; - DWORD SQLSelects; - DWORD SQLSelectRows; - DWORD Transactions; - DWORD SQLPrepares; - DWORD ExecDirects; - DWORD SQLExecutes; - DWORD CursorOpens; - DWORD CursorSize; - DWORD CursorUsed; - LDOUBLE PercentCursorUsed; - LDOUBLE AvgFetchTime; - LDOUBLE AvgCursorSize; - LDOUBLE AvgCursorUsed; - DWORD SQLFetchTime; - DWORD SQLFetchCount; - DWORD CurrentStmtCount; - DWORD MaxOpenStmt; - DWORD SumOpenStmt; - // Connection Statistics - DWORD CurrentConnectionCount; - DWORD MaxConnectionsOpened; - DWORD SumConnectionsOpened; - DWORD SumConnectiontime; - LDOUBLE AvgTimeOpened; - // Network Statistics - DWORD ServerRndTrips; - DWORD BuffersSent; - DWORD BuffersRec; - DWORD BytesSent; - DWORD BytesRec; - // Time Statistics; - DWORD msExecutionTime; - DWORD msNetWorkServerTime; -} SQLPERF; -// The following are options for SQL_COPT_SS_PERF_DATA and SQL_COPT_SS_PERF_QUERY + /************************************************************************** + This struct is a global used for gathering statistical data on the driver. + Access to this structure is controlled via the pStatCrit; + ***************************************************************************/ + typedef struct sqlperf + { + // Application Profile Statistics + DWORD TimerResolution; + DWORD SQLidu; + DWORD SQLiduRows; + DWORD SQLSelects; + DWORD SQLSelectRows; + DWORD Transactions; + DWORD SQLPrepares; + DWORD ExecDirects; + DWORD SQLExecutes; + DWORD CursorOpens; + DWORD CursorSize; + DWORD CursorUsed; + LDOUBLE PercentCursorUsed; + LDOUBLE AvgFetchTime; + LDOUBLE AvgCursorSize; + LDOUBLE AvgCursorUsed; + DWORD SQLFetchTime; + DWORD SQLFetchCount; + DWORD CurrentStmtCount; + DWORD MaxOpenStmt; + DWORD SumOpenStmt; + // Connection Statistics + DWORD CurrentConnectionCount; + DWORD MaxConnectionsOpened; + DWORD SumConnectionsOpened; + DWORD SumConnectiontime; + LDOUBLE AvgTimeOpened; + // Network Statistics + DWORD ServerRndTrips; + DWORD BuffersSent; + DWORD BuffersRec; + DWORD BytesSent; + DWORD BytesRec; + // Time Statistics; + DWORD msExecutionTime; + DWORD msNetWorkServerTime; + } SQLPERF; + // The following are options for SQL_COPT_SS_PERF_DATA and SQL_COPT_SS_PERF_QUERY #define SQL_PERF_START 1 // Starts the driver sampling performance data. #define SQL_PERF_STOP 2 // Stops the counters from sampling performance data. -// The following are defines for SQL_COPT_SS_PERF_DATA_LOG + // The following are defines for SQL_COPT_SS_PERF_DATA_LOG #define SQL_SS_DL_DEFAULT TEXT("STATS.LOG") -// The following are defines for SQL_COPT_SS_PERF_QUERY_LOG + // The following are defines for SQL_COPT_SS_PERF_QUERY_LOG #define SQL_SS_QL_DEFAULT TEXT("QUERY.LOG") -// The following are defines for SQL_COPT_SS_PERF_QUERY_INTERVAL + // The following are defines for SQL_COPT_SS_PERF_QUERY_INTERVAL #define SQL_SS_QI_DEFAULT 30000 // 30,000 milliseconds #ifndef SQLNCLI_NO_BCP -// Define the symbol SQLNCLI_NO_BCP if you are not using BCP in your application -// and you want to exclude the BCP-related definitions in this header file. + // Define the symbol SQLNCLI_NO_BCP if you are not using BCP in your application + // and you want to exclude the BCP-related definitions in this header file. -// ODBC BCP prototypes and defines -// Return codes + // ODBC BCP prototypes and defines + // Return codes #define SUCCEED 1 #define FAIL 0 #define SUCCEED_ABORT 2 #define SUCCEED_ASYNC 3 -// Transfer directions + // Transfer directions #define DB_IN 1 // Transfer from client to server #define DB_OUT 2 // Transfer from server to client -// bcp_control option + // bcp_control option #define BCPMAXERRS 1 // Sets max errors allowed #define BCPFIRST 2 // Sets first row to be copied out #define BCPLAST 3 // Sets number of rows to be copied out @@ -848,14 +876,14 @@ typedef struct sqlperf #define BCPLASTEX 18 // Ending Row for BCP operation (64 bit) #define BCPROWCOUNT 19 // Total Number of Rows Copied (64 bit) #define BCPDELAYREADFMT 20 // Delay reading format file unil bcp_exec -// BCPFILECP values -// Any valid code page that is installed on the client can be passed plus: + // BCPFILECP values + // Any valid code page that is installed on the client can be passed plus: #define BCPFILECP_ACP 0 // Data in file is in Windows code page #define BCPFILECP_OEMCP 1 // Data in file is in OEM code page (default) #define BCPFILECP_RAW (-1)// Data in file is in Server code page (no conversion) -// bcp_collen definition + // bcp_collen definition #define SQL_VARLEN_DATA (-10) // Use default length for column -// BCP column format properties + // BCP column format properties #define BCP_FMT_TYPE 0x01 #define BCP_FMT_INDICATOR_LEN 0x02 #define BCP_FMT_DATA_LEN 0x03 @@ -863,7 +891,7 @@ typedef struct sqlperf #define BCP_FMT_SERVER_COL 0x05 #define BCP_FMT_COLLATION 0x06 #define BCP_FMT_COLLATION_ID 0x07 -// bcp_setbulkmode properties + // bcp_setbulkmode properties #define BCP_OUT_CHARACTER_MODE 0x01 #define BCP_OUT_WIDE_CHARACTER_MODE 0x02 #define BCP_OUT_NATIVE_TEXT_MODE 0x03 @@ -871,31 +899,31 @@ typedef struct sqlperf -// BCP functions -DBINT SQL_API bcp_batch (HDBC); -RETCODE SQL_API bcp_bind (HDBC, LPCBYTE, INT, DBINT, LPCBYTE, INT, INT, INT); -RETCODE SQL_API bcp_colfmt (HDBC, INT, BYTE, INT, DBINT, LPCBYTE, INT, INT); -RETCODE SQL_API bcp_collen (HDBC, DBINT, INT); -RETCODE SQL_API bcp_colptr (HDBC, LPCBYTE, INT); -RETCODE SQL_API bcp_columns (HDBC, INT); -RETCODE SQL_API bcp_control (HDBC, INT, void *); -DBINT SQL_API bcp_done (HDBC); -RETCODE SQL_API bcp_exec (HDBC, LPDBINT); -RETCODE SQL_API bcp_getcolfmt (HDBC, INT, INT, void *, INT, INT *); -RETCODE SQL_API bcp_initA (HDBC, LPCSTR, LPCSTR, LPCSTR, INT); -RETCODE SQL_API bcp_initW (HDBC, LPCWSTR, LPCWSTR, LPCWSTR, INT); -RETCODE SQL_API bcp_moretext (HDBC, DBINT, LPCBYTE); -RETCODE SQL_API bcp_readfmtA (HDBC, LPCSTR); -RETCODE SQL_API bcp_readfmtW (HDBC, LPCWSTR); -RETCODE SQL_API bcp_sendrow (HDBC); -RETCODE SQL_API bcp_setbulkmode (HDBC, INT, __in_bcount(cbField) void*, INT cbField, __in_bcount(cbRow) void *, INT cbRow); -RETCODE SQL_API bcp_setcolfmt (HDBC, INT, INT, void *, INT); -RETCODE SQL_API bcp_writefmtA (HDBC, LPCSTR); -RETCODE SQL_API bcp_writefmtW (HDBC, LPCWSTR); -CHAR* SQL_API dbprtypeA (INT); -WCHAR* SQL_API dbprtypeW (INT); -CHAR* SQL_API bcp_gettypenameA (INT, DBBOOL); -WCHAR* SQL_API bcp_gettypenameW (INT, DBBOOL); + // BCP functions + DBINT SQL_API bcp_batch(HDBC); + RETCODE SQL_API bcp_bind(HDBC, LPCBYTE, INT, DBINT, LPCBYTE, INT, INT, INT); + RETCODE SQL_API bcp_colfmt(HDBC, INT, BYTE, INT, DBINT, LPCBYTE, INT, INT); + RETCODE SQL_API bcp_collen(HDBC, DBINT, INT); + RETCODE SQL_API bcp_colptr(HDBC, LPCBYTE, INT); + RETCODE SQL_API bcp_columns(HDBC, INT); + RETCODE SQL_API bcp_control(HDBC, INT, void *); + DBINT SQL_API bcp_done(HDBC); + RETCODE SQL_API bcp_exec(HDBC, LPDBINT); + RETCODE SQL_API bcp_getcolfmt(HDBC, INT, INT, void *, INT, INT *); + RETCODE SQL_API bcp_initA(HDBC, LPCSTR, LPCSTR, LPCSTR, INT); + RETCODE SQL_API bcp_initW(HDBC, LPCWSTR, LPCWSTR, LPCWSTR, INT); + RETCODE SQL_API bcp_moretext(HDBC, DBINT, LPCBYTE); + RETCODE SQL_API bcp_readfmtA(HDBC, LPCSTR); + RETCODE SQL_API bcp_readfmtW(HDBC, LPCWSTR); + RETCODE SQL_API bcp_sendrow(HDBC); + RETCODE SQL_API bcp_setbulkmode(HDBC, INT, __in_bcount(cbField) void*, INT cbField, __in_bcount(cbRow) void *, INT cbRow); + RETCODE SQL_API bcp_setcolfmt(HDBC, INT, INT, void *, INT); + RETCODE SQL_API bcp_writefmtA(HDBC, LPCSTR); + RETCODE SQL_API bcp_writefmtW(HDBC, LPCWSTR); + CHAR* SQL_API dbprtypeA(INT); + WCHAR* SQL_API dbprtypeW(INT); + CHAR* SQL_API bcp_gettypenameA(INT, DBBOOL); + WCHAR* SQL_API bcp_gettypenameW(INT, DBBOOL); #ifdef UNICODE #define bcp_init bcp_initW @@ -915,9 +943,9 @@ WCHAR* SQL_API bcp_gettypenameW (INT, DBBOOL); #endif // SQLNCLI_NO_BCP -// The following options have been deprecated + // The following options have been deprecated #define SQL_FAST_CONNECT (SQL_COPT_SS_BASE+0) -// Defines for use with SQL_FAST_CONNECT - only useable before connecting + // Defines for use with SQL_FAST_CONNECT - only useable before connecting #define SQL_FC_OFF 0L // Fast connect is off #define SQL_FC_ON 1L // Fast connect is on #define SQL_FC_DEFAULT SQL_FC_OFF @@ -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" @@ -941,12 +1023,12 @@ extern "C" { #endif #include -//The following facilitates opening a handle to a SQL filestream -typedef enum _SQL_FILESTREAM_DESIRED_ACCESS { - SQL_FILESTREAM_READ = 0, - SQL_FILESTREAM_WRITE = 1, - SQL_FILESTREAM_READWRITE = 2 -} SQL_FILESTREAM_DESIRED_ACCESS; + //The following facilitates opening a handle to a SQL filestream + typedef enum _SQL_FILESTREAM_DESIRED_ACCESS { + SQL_FILESTREAM_READ = 0, + SQL_FILESTREAM_WRITE = 1, + SQL_FILESTREAM_READWRITE = 2 + } SQL_FILESTREAM_DESIRED_ACCESS; #define SQL_FILESTREAM_OPEN_FLAG_ASYNC 0x00000001L #define SQL_FILESTREAM_OPEN_FLAG_NO_BUFFERING 0x00000002L #define SQL_FILESTREAM_OPEN_FLAG_NO_WRITE_THROUGH 0x00000004L @@ -954,14 +1036,14 @@ typedef enum _SQL_FILESTREAM_DESIRED_ACCESS { #define SQL_FILESTREAM_OPEN_FLAG_RANDOM_ACCESS 0x00000010L -HANDLE __stdcall OpenSqlFilestream ( - LPCWSTR FilestreamPath, - SQL_FILESTREAM_DESIRED_ACCESS DesiredAccess, - ULONG OpenOptions, - __in_bcount(FilestreamTransactionContextLength) - LPBYTE FilestreamTransactionContext, - SSIZE_T FilestreamTransactionContextLength, - PLARGE_INTEGER AllocationSize); + HANDLE __stdcall OpenSqlFilestream( + LPCWSTR FilestreamPath, + SQL_FILESTREAM_DESIRED_ACCESS DesiredAccess, + ULONG OpenOptions, + __in_bcount(FilestreamTransactionContextLength) + LPBYTE FilestreamTransactionContext, + SSIZE_T FilestreamTransactionContextLength, + PLARGE_INTEGER AllocationSize); #define FSCTL_SQL_FILESTREAM_FETCH_OLD_CONTENT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 2392, METHOD_BUFFERED, FILE_ANY_ACCESS) #ifdef __cplusplus @@ -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,491 +1073,504 @@ HANDLE __stdcall OpenSqlFilestream ( extern "C" { #endif + struct _CERT_CONTEXT; + typedef _CERT_CONTEXT CERT_CONTEXT; + typedef const CERT_CONTEXT *PCCERT_CONTEXT; -// Recommended buffer size to store a LocalDB connection string + // 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 -// type definition for LocalDBCreateInstance function -typedef HRESULT __cdecl FnLocalDBCreateInstance ( - // I the LocalDB version (e.g. 11.0 or 11.0.1094.2) - __in_z PCWSTR wszVersion, - // I the instance name - __in_z PCWSTR pInstanceName, - // I reserved for the future use. Currently should be set to 0. - __in DWORD dwFlags -); + // type definition for LocalDBCreateInstance function + typedef HRESULT __cdecl FnLocalDBCreateInstance( + // I the LocalDB version (e.g. 11.0 or 11.0.1094.2) + __in_z PCWSTR wszVersion, + // I the instance name + __in_z PCWSTR pInstanceName, + // I reserved for the future use. Currently should be set to 0. + __in DWORD dwFlags + ); -// type definition for pointer to LocalDBCreateInstance function -typedef FnLocalDBCreateInstance* PFnLocalDBCreateInstance; + // type definition for pointer to LocalDBCreateInstance function + typedef FnLocalDBCreateInstance* PFnLocalDBCreateInstance; -// type definition for LocalDBStartInstance function -typedef HRESULT __cdecl FnLocalDBStartInstance ( - // I the LocalDB instance name - __in_z PCWSTR pInstanceName, - // I reserved for the future use. Currently should be set to 0. - __in DWORD dwFlags, - // O the buffer to store the connection string to the LocalDB instance - __out_ecount_z_opt(*lpcchSqlConnection) LPWSTR wszSqlConnection, - // I/O on input has the size of the wszSqlConnection buffer in characters. On output, if the given buffer size is - // too small, has the buffer size required, in characters, including trailing null. - __inout_opt LPDWORD lpcchSqlConnection -); + // type definition for LocalDBStartInstance function + typedef HRESULT __cdecl FnLocalDBStartInstance( + // I the LocalDB instance name + __in_z PCWSTR pInstanceName, + // I reserved for the future use. Currently should be set to 0. + __in DWORD dwFlags, + // O the buffer to store the connection string to the LocalDB instance + __out_ecount_z_opt(*lpcchSqlConnection) LPWSTR wszSqlConnection, + // I/O on input has the size of the wszSqlConnection buffer in characters. On output, if the given buffer size is + // too small, has the buffer size required, in characters, including trailing null. + __inout_opt LPDWORD lpcchSqlConnection + ); -// type definition for pointer to LocalDBStartInstance function -typedef FnLocalDBStartInstance* PFnLocalDBStartInstance; + // type definition for pointer to LocalDBStartInstance function + typedef FnLocalDBStartInstance* PFnLocalDBStartInstance; -// Flags for the LocalDBFormatMessage function + // Flags for the LocalDBFormatMessage function #define LOCALDB_TRUNCATE_ERR_MESSAGE 0x0001L -// type definition for LocalDBFormatMessage function -typedef HRESULT __cdecl FnLocalDBFormatMessage( - // I the LocalDB error code - __in HRESULT hrLocalDB, - // I Available flags: - // LOCALDB_TRUNCATE_ERR_MESSAGE - if the input buffer is too short, - // the error message will be truncated to fit into the buffer - __in DWORD dwFlags, - // I Language desired (LCID) or 0 (in which case Win32 FormatMessage order is used) - __in DWORD dwLanguageId, - // O the buffer to store the LocalDB error message - __out_ecount_z(*lpcchMessage) LPWSTR wszMessage, - // I/O on input has the size of the wszMessage buffer in characters. On output, if the given buffer size is - // too small, has the buffer size required, in characters, including trailing null. If the function succeeds - // contains the number of characters in the message, excluding the trailing null - __inout LPDWORD lpcchMessage -); + // type definition for LocalDBFormatMessage function + typedef HRESULT __cdecl FnLocalDBFormatMessage( + // I the LocalDB error code + __in HRESULT hrLocalDB, + // I Available flags: + // LOCALDB_TRUNCATE_ERR_MESSAGE - if the input buffer is too short, + // the error message will be truncated to fit into the buffer + __in DWORD dwFlags, + // I Language desired (LCID) or 0 (in which case Win32 FormatMessage order is used) + __in DWORD dwLanguageId, + // O the buffer to store the LocalDB error message + __out_ecount_z(*lpcchMessage) LPWSTR wszMessage, + // I/O on input has the size of the wszMessage buffer in characters. On output, if the given buffer size is + // too small, has the buffer size required, in characters, including trailing null. If the function succeeds + // contains the number of characters in the message, excluding the trailing null + __inout LPDWORD lpcchMessage + ); -// type definition for function pointer to LocalDBFormatMessage function -typedef FnLocalDBFormatMessage* PFnLocalDBFormatMessage; + // type definition for function pointer to LocalDBFormatMessage function + typedef FnLocalDBFormatMessage* PFnLocalDBFormatMessage; -// MessageId: LOCALDB_ERROR_NOT_INSTALLED -// -// MessageText: -// -// LocalDB is not installed. -// + // MessageId: LOCALDB_ERROR_NOT_INSTALLED + // + // MessageText: + // + // LocalDB is not installed. + // #define LOCALDB_ERROR_NOT_INSTALLED ((HRESULT)0x89C50116L) -//--------------------------------------------------------------------- -// Function: LocalDBCreateInstance -// -// Description: This function will create the new LocalDB instance. -// -// Available Flags: -// No flags available. Reserved for future use. -// -// Return Values: -// S_OK, if the function succeeds -// LOCALDB_ERROR_INVALID_PARAM_INSTANCE_NAME, if the instance name parameter is invalid -// LOCALDB_ERROR_INVALID_PARAM_VERSION, if the version parameter is invalid -// LOCALDB_ERROR_INVALID_PARAM_FLAGS, if the flags are invalid -// LOCALDB_ERROR_INVALID_OPERATION, if the user tries to create a default instance -// LOCALDB_ERROR_INSTANCE_FOLDER_PATH_TOO_LONG, if the path where instance should be stored is longer than MAX_PATH -// LOCALDB_ERROR_VERSION_REQUESTED_NOT_INSTALLED, if the specified service level is not installed -// LOCALDB_ERROR_INSTANCE_FOLDER_ALREADY_EXISTS, if the instance folder already exists and is not empty -// LOCALDB_ERROR_INSTANCE_EXISTS_WITH_LOWER_VERSION, if the specified instance already exists but with lower version -// LOCALDB_ERROR_CANNOT_CREATE_INSTANCE_FOLDER, if a folder cannot be created under %userprofile% -// LOCALDB_ERROR_CANNOT_GET_USER_PROFILE_FOLDER, if a user profile folder cannot be retrieved -// LOCALDB_ERROR_CANNOT_ACCESS_INSTANCE_FOLDER, if a instance folder cannot be accessed -// LOCALDB_ERROR_CANNOT_ACCESS_INSTANCE_REGISTRY, if a instance registry cannot be accessed -// LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurred. See event log for details -// LOCALDB_ERROR_CANNOT_MODIFY_INSTANCE_REGISTRY, if an instance registry cannot be modified -// LOCALDB_ERROR_CANNOT_CREATE_SQL_PROCESS, if a process for Sql Server cannot be created -// LOCALDB_ERROR_SQL_SERVER_STARTUP_FAILED, if a Sql Server process is started but Sql Server startup failed. -// LOCALDB_ERROR_INSTANCE_CONFIGURATION_CORRUPT, if a instance configuration is corrupted -// -FnLocalDBCreateInstance LocalDBCreateInstance; + //--------------------------------------------------------------------- + // Function: LocalDBCreateInstance + // + // Description: This function will create the new LocalDB instance. + // + // Available Flags: + // No flags available. Reserved for future use. + // + // Return Values: + // S_OK, if the function succeeds + // LOCALDB_ERROR_INVALID_PARAM_INSTANCE_NAME, if the instance name parameter is invalid + // LOCALDB_ERROR_INVALID_PARAM_VERSION, if the version parameter is invalid + // LOCALDB_ERROR_INVALID_PARAM_FLAGS, if the flags are invalid + // LOCALDB_ERROR_INVALID_OPERATION, if the user tries to create a default instance + // LOCALDB_ERROR_INSTANCE_FOLDER_PATH_TOO_LONG, if the path where instance should be stored is longer than MAX_PATH + // LOCALDB_ERROR_VERSION_REQUESTED_NOT_INSTALLED, if the specified service level is not installed + // LOCALDB_ERROR_INSTANCE_FOLDER_ALREADY_EXISTS, if the instance folder already exists and is not empty + // LOCALDB_ERROR_INSTANCE_EXISTS_WITH_LOWER_VERSION, if the specified instance already exists but with lower version + // LOCALDB_ERROR_CANNOT_CREATE_INSTANCE_FOLDER, if a folder cannot be created under %userprofile% + // LOCALDB_ERROR_CANNOT_GET_USER_PROFILE_FOLDER, if a user profile folder cannot be retrieved + // LOCALDB_ERROR_CANNOT_ACCESS_INSTANCE_FOLDER, if a instance folder cannot be accessed + // LOCALDB_ERROR_CANNOT_ACCESS_INSTANCE_REGISTRY, if a instance registry cannot be accessed + // LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurred. See event log for details + // LOCALDB_ERROR_CANNOT_MODIFY_INSTANCE_REGISTRY, if an instance registry cannot be modified + // LOCALDB_ERROR_CANNOT_CREATE_SQL_PROCESS, if a process for Sql Server cannot be created + // LOCALDB_ERROR_SQL_SERVER_STARTUP_FAILED, if a Sql Server process is started but Sql Server startup failed. + // LOCALDB_ERROR_INSTANCE_CONFIGURATION_CORRUPT, if a instance configuration is corrupted + // + FnLocalDBCreateInstance LocalDBCreateInstance; -//--------------------------------------------------------------------- -// Function: LocalDBStartInstance -// -// Description: This function will start the given LocalDB instance. -// -// Return Values: -// S_OK, if the function succeeds -// LOCALDB_ERROR_UNKNOWN_INSTANCE, if the specified instance doesn't exist -// LOCALDB_ERROR_INVALID_PARAM_INSTANCE_NAME, if the instance name parameter is invalid -// LOCALDB_ERROR_INVALID_PARAM_CONNECTION, if the wszSqlConnection parameter is NULL -// LOCALDB_ERROR_INVALID_PARAM_FLAGS, if the flags are invalid -// LOCALDB_ERROR_INSUFFICIENT_BUFFER, if the buffer wszSqlConnection is too small -// LOCALDB_ERROR_INSTANCE_FOLDER_PATH_TOO_LONG, if the path where instance should be stored is longer than MAX_PATH + //--------------------------------------------------------------------- + // Function: LocalDBStartInstance + // + // Description: This function will start the given LocalDB instance. + // + // Return Values: + // S_OK, if the function succeeds + // LOCALDB_ERROR_UNKNOWN_INSTANCE, if the specified instance doesn't exist + // LOCALDB_ERROR_INVALID_PARAM_INSTANCE_NAME, if the instance name parameter is invalid + // LOCALDB_ERROR_INVALID_PARAM_CONNECTION, if the wszSqlConnection parameter is NULL + // LOCALDB_ERROR_INVALID_PARAM_FLAGS, if the flags are invalid + // LOCALDB_ERROR_INSUFFICIENT_BUFFER, if the buffer wszSqlConnection is too small + // LOCALDB_ERROR_INSTANCE_FOLDER_PATH_TOO_LONG, if the path where instance should be stored is longer than MAX_PATH -// LOCALDB_ERROR_CANNOT_GET_USER_PROFILE_FOLDER, if a user profile folder cannot be retrieved -// LOCALDB_ERROR_CANNOT_ACCESS_INSTANCE_FOLDER, if a instance folder cannot be accessed -// LOCALDB_ERROR_CANNOT_ACCESS_INSTANCE_REGISTRY, if a instance registry cannot be accessed -// LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurred. See event log for details -// LOCALDB_ERROR_CANNOT_MODIFY_INSTANCE_REGISTRY, if an instance registry cannot be modified -// LOCALDB_ERROR_CANNOT_CREATE_SQL_PROCESS, if a process for Sql Server cannot be created -// LOCALDB_ERROR_SQL_SERVER_STARTUP_FAILED, if a Sql Server process is started but Sql Server startup failed. -// LOCALDB_ERROR_INSTANCE_CONFIGURATION_CORRUPT, if a instance configuration is corrupted -// -FnLocalDBStartInstance LocalDBStartInstance; + // LOCALDB_ERROR_CANNOT_GET_USER_PROFILE_FOLDER, if a user profile folder cannot be retrieved + // LOCALDB_ERROR_CANNOT_ACCESS_INSTANCE_FOLDER, if a instance folder cannot be accessed + // LOCALDB_ERROR_CANNOT_ACCESS_INSTANCE_REGISTRY, if a instance registry cannot be accessed + // LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurred. See event log for details + // LOCALDB_ERROR_CANNOT_MODIFY_INSTANCE_REGISTRY, if an instance registry cannot be modified + // LOCALDB_ERROR_CANNOT_CREATE_SQL_PROCESS, if a process for Sql Server cannot be created + // LOCALDB_ERROR_SQL_SERVER_STARTUP_FAILED, if a Sql Server process is started but Sql Server startup failed. + // LOCALDB_ERROR_INSTANCE_CONFIGURATION_CORRUPT, if a instance configuration is corrupted + // + FnLocalDBStartInstance LocalDBStartInstance; -// type definition for LocalDBStopInstance function -typedef HRESULT __cdecl FnLocalDBStopInstance ( - // I the LocalDB instance name - __in_z PCWSTR pInstanceName, - // I Available flags: - // LOCALDB_SHUTDOWN_KILL_PROCESS - force the instance to stop immediately - // LOCALDB_SHUTDOWN_WITH_NOWAIT - shutdown the instance with NOWAIT option - __in DWORD dwFlags, - // I the time in seconds to wait this operation to complete. If this value is 0, this function will return immediately - // without waiting for LocalDB instance to stop - __in ULONG ulTimeout -); + // type definition for LocalDBStopInstance function + typedef HRESULT __cdecl FnLocalDBStopInstance( + // I the LocalDB instance name + __in_z PCWSTR pInstanceName, + // I Available flags: + // LOCALDB_SHUTDOWN_KILL_PROCESS - force the instance to stop immediately + // LOCALDB_SHUTDOWN_WITH_NOWAIT - shutdown the instance with NOWAIT option + __in DWORD dwFlags, + // I the time in seconds to wait this operation to complete. If this value is 0, this function will return immediately + // without waiting for LocalDB instance to stop + __in ULONG ulTimeout + ); -// type definition for pointer to LocalDBStopInstance function -typedef FnLocalDBStopInstance* PFnLocalDBStopInstance; + // type definition for pointer to LocalDBStopInstance function + typedef FnLocalDBStopInstance* PFnLocalDBStopInstance; -// Flags for the StopLocalDBInstance function + // Flags for the StopLocalDBInstance function #define LOCALDB_SHUTDOWN_KILL_PROCESS 0x0001L #define LOCALDB_SHUTDOWN_WITH_NOWAIT 0x0002L -//--------------------------------------------------------------------- -// Function: LocalDBStopInstance -// -// Description: This function will shutdown the given LocalDB instance. -// If the flag LOCALDB_SHUTDOWN_KILL_PROCESS is set, the LocalDB instance will be killed immediately. -// IF the flag LOCALDB_SHUTDOWN_WITH_NOWAIT is set, the LocalDB instance will shutdown with NOWAIT option. -// -// Return Values: -// S_OK, if the function succeeds -// LOCALDB_ERROR_UNKNOWN_INSTANCE, if the specified instance doesn't exist -// LOCALDB_ERROR_INVALID_PARAM_INSTANCE_NAME, if the instance name parameter is invalid -// LOCALDB_ERROR_INVALID_PARAM_FLAGS, if the flags are invalid -// LOCALDB_ERROR_WAIT_TIMEOUT - if this function has not finished in given time -// LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurred. See event log for details -// -FnLocalDBStopInstance LocalDBStopInstance; + //--------------------------------------------------------------------- + // Function: LocalDBStopInstance + // + // Description: This function will shutdown the given LocalDB instance. + // If the flag LOCALDB_SHUTDOWN_KILL_PROCESS is set, the LocalDB instance will be killed immediately. + // IF the flag LOCALDB_SHUTDOWN_WITH_NOWAIT is set, the LocalDB instance will shutdown with NOWAIT option. + // + // Return Values: + // S_OK, if the function succeeds + // LOCALDB_ERROR_UNKNOWN_INSTANCE, if the specified instance doesn't exist + // LOCALDB_ERROR_INVALID_PARAM_INSTANCE_NAME, if the instance name parameter is invalid + // LOCALDB_ERROR_INVALID_PARAM_FLAGS, if the flags are invalid + // LOCALDB_ERROR_WAIT_TIMEOUT - if this function has not finished in given time + // LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurred. See event log for details + // + FnLocalDBStopInstance LocalDBStopInstance; -// type definition for LocalDBDeleteInstance function -typedef HRESULT __cdecl FnLocalDBDeleteInstance ( - // I the LocalDB instance name - __in_z PCWSTR pInstanceName, - // I reserved for the future use. Currently should be set to 0. - __in DWORD dwFlags -); + // type definition for LocalDBDeleteInstance function + typedef HRESULT __cdecl FnLocalDBDeleteInstance( + // I the LocalDB instance name + __in_z PCWSTR pInstanceName, + // I reserved for the future use. Currently should be set to 0. + __in DWORD dwFlags + ); -// type definition for pointer to LocalDBDeleteInstance function -typedef FnLocalDBDeleteInstance* PFnLocalDBDeleteInstance; + // type definition for pointer to LocalDBDeleteInstance function + typedef FnLocalDBDeleteInstance* PFnLocalDBDeleteInstance; -//--------------------------------------------------------------------- -// Function: LocalDBDeleteInstance -// -// Description: This function will remove the given LocalDB instance. If the given instance is running this function will -// fail with the error code LOCALDB_ERROR_INSTANCE_BUSY. -// -// Return Values: -// S_OK, if the function succeeds -// LOCALDB_ERROR_INVALID_PARAM_INSTANCE_NAME, if the instance name parameter is invalid -// LOCALDB_ERROR_INVALID_PARAM_FLAGS, if the flags are invalid -// LOCALDB_ERROR_UNKNOWN_INSTANCE, if the specified instance doesn't exist -// LOCALDB_ERROR_INSTANCE_BUSY, if the given instance is running -// LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurred. See event log for details -// -FnLocalDBDeleteInstance LocalDBDeleteInstance; + //--------------------------------------------------------------------- + // Function: LocalDBDeleteInstance + // + // Description: This function will remove the given LocalDB instance. If the given instance is running this function will + // fail with the error code LOCALDB_ERROR_INSTANCE_BUSY. + // + // Return Values: + // S_OK, if the function succeeds + // LOCALDB_ERROR_INVALID_PARAM_INSTANCE_NAME, if the instance name parameter is invalid + // LOCALDB_ERROR_INVALID_PARAM_FLAGS, if the flags are invalid + // LOCALDB_ERROR_UNKNOWN_INSTANCE, if the specified instance doesn't exist + // LOCALDB_ERROR_INSTANCE_BUSY, if the given instance is running + // LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurred. See event log for details + // + FnLocalDBDeleteInstance LocalDBDeleteInstance; -// Function: LocalDBFormatMessage -// -// Description: This function will return the localized textual description for the given LocalDB error -// -// Available Flags: -// LOCALDB_TRUNCATE_ERR_MESSAGE - the error message should be truncated to fit into the provided buffer -// -// Return Value: -// S_OK, if the function succeeds -// -// LOCALDB_ERROR_UNKNOWN_HRESULT, if the given HRESULT is unknown -// LOCALDB_ERROR_UNKNOWN_LANGUAGE_ID, if the given language id is unknown (0 is recommended for the // default language) -// LOCALDB_ERROR_UNKNOWN_ERROR_CODE, if the LocalDB error code is unknown -// LOCALDB_ERROR_INVALID_PARAM_FLAGS, if the flags are invalid -// LOCALDB_ERROR_INSUFFICIENT_BUFFER, if the input buffer is too short and LOCALDB_TRUNCATE_ERR_MESSAGE flag -// is not set -// LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurred. See event log for details -// -FnLocalDBFormatMessage LocalDBFormatMessage; + // Function: LocalDBFormatMessage + // + // Description: This function will return the localized textual description for the given LocalDB error + // + // Available Flags: + // LOCALDB_TRUNCATE_ERR_MESSAGE - the error message should be truncated to fit into the provided buffer + // + // Return Value: + // S_OK, if the function succeeds + // + // LOCALDB_ERROR_UNKNOWN_HRESULT, if the given HRESULT is unknown + // LOCALDB_ERROR_UNKNOWN_LANGUAGE_ID, if the given language id is unknown (0 is recommended for the // default language) + // LOCALDB_ERROR_UNKNOWN_ERROR_CODE, if the LocalDB error code is unknown + // LOCALDB_ERROR_INVALID_PARAM_FLAGS, if the flags are invalid + // LOCALDB_ERROR_INSUFFICIENT_BUFFER, if the input buffer is too short and LOCALDB_TRUNCATE_ERR_MESSAGE flag + // is not set + // LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurred. See event log for details + // + FnLocalDBFormatMessage LocalDBFormatMessage; #define MAX_LOCALDB_INSTANCE_NAME_LENGTH 128 #define MAX_LOCALDB_PARENT_INSTANCE_LENGTH MAX_INSTANCE_NAME -typedef WCHAR TLocalDBInstanceName[MAX_LOCALDB_INSTANCE_NAME_LENGTH + 1]; -typedef TLocalDBInstanceName* PTLocalDBInstanceName; + typedef WCHAR TLocalDBInstanceName[MAX_LOCALDB_INSTANCE_NAME_LENGTH + 1]; + typedef TLocalDBInstanceName* PTLocalDBInstanceName; -// type definition for LocalDBGetInstances function -typedef HRESULT __cdecl FnLocalDBGetInstances( - // O buffer for a LocalDB instance names - __out PTLocalDBInstanceName pInstanceNames, - // I/O on input has the number slots for instance names in the pInstanceNames buffer. On output, - // has the number of existing LocalDB instances - __inout LPDWORD lpdwNumberOfInstances -); + // type definition for LocalDBGetInstances function + typedef HRESULT __cdecl FnLocalDBGetInstances( + // O buffer for a LocalDB instance names + __out PTLocalDBInstanceName pInstanceNames, + // I/O on input has the number slots for instance names in the pInstanceNames buffer. On output, + // has the number of existing LocalDB instances + __inout LPDWORD lpdwNumberOfInstances + ); -// type definition for pointer to LocalDBGetInstances function -typedef FnLocalDBGetInstances* PFnLocalDBGetInstances; + // type definition for pointer to LocalDBGetInstances function + typedef FnLocalDBGetInstances* PFnLocalDBGetInstances; -// Function: LocalDBGetInstances -// -// Description: This function returns names for all existing Local DB instances -// -// Usage Example: -// DWORD dwN = 0; -// LocalDBGetInstances(NULL, &dwN); + // Function: LocalDBGetInstances + // + // Description: This function returns names for all existing Local DB instances + // + // Usage Example: + // DWORD dwN = 0; + // LocalDBGetInstances(NULL, &dwN); -// PTLocalDBInstanceName insts = (PTLocalDBInstanceName) malloc(dwN * sizeof(TLocalDBInstanceName)); -// LocalDBGetInstances(insts, &dwN); + // PTLocalDBInstanceName insts = (PTLocalDBInstanceName) malloc(dwN * sizeof(TLocalDBInstanceName)); + // LocalDBGetInstances(insts, &dwN); -// for (int i = 0; i < dwN; i++) -// wprintf(L"%s\n", insts[i]); -// -// Return values: -// S_OK, if the function succeeds -// -// LOCALDB_ERROR_INSUFFICIENT_BUFFER, the given buffer is to small -// LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurred. See event log for details -// -FnLocalDBGetInstances LocalDBGetInstances; + // for (int i = 0; i < dwN; i++) + // wprintf(L"%s\n", insts[i]); + // + // Return values: + // S_OK, if the function succeeds + // + // LOCALDB_ERROR_INSUFFICIENT_BUFFER, the given buffer is to small + // LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurred. See event log for details + // + FnLocalDBGetInstances LocalDBGetInstances; -// SID string format: S - Revision(1B) - Authority ID (6B) {- Sub authority ID (4B)} * max 15 sub-authorities = 1 + 1 + 3 + 1 + 15 + (1 + 10) * 15 + // SID string format: S - Revision(1B) - Authority ID (6B) {- Sub authority ID (4B)} * max 15 sub-authorities = 1 + 1 + 3 + 1 + 15 + (1 + 10) * 15 #define MAX_STRING_SID_LENGTH 186 #pragma pack(push) #pragma pack(8) -// DEVNOTE: If you want to modify this structure please read DEVNOTEs on top of function LocalDBGetInstanceInfo in sqluserinstance.cpp file. -// -typedef struct _LocalDBInstanceInfo -{ - DWORD cbLocalDBInstanceInfoSize; - TLocalDBInstanceName wszInstanceName; - BOOL bExists; - BOOL bConfigurationCorrupted; - BOOL bIsRunning; - DWORD dwMajor; - DWORD dwMinor; - DWORD dwBuild; - DWORD dwRevision; - FILETIME ftLastStartDateUTC; - WCHAR wszConnection[LOCALDB_MAX_SQLCONNECTION_BUFFER_SIZE]; - BOOL bIsShared; - TLocalDBInstanceName wszSharedInstanceName; - WCHAR wszOwnerSID[MAX_STRING_SID_LENGTH + 1]; - BOOL bIsAutomatic; -} LocalDBInstanceInfo; + // DEVNOTE: If you want to modify this structure please read DEVNOTEs on top of function LocalDBGetInstanceInfo in sqluserinstance.cpp file. + // + typedef struct _LocalDBInstanceInfo + { + DWORD cbLocalDBInstanceInfoSize; + TLocalDBInstanceName wszInstanceName; + BOOL bExists; + BOOL bConfigurationCorrupted; + BOOL bIsRunning; + DWORD dwMajor; + DWORD dwMinor; + DWORD dwBuild; + DWORD dwRevision; + FILETIME ftLastStartDateUTC; + WCHAR wszConnection[LOCALDB_MAX_SQLCONNECTION_BUFFER_SIZE]; + BOOL bIsShared; + TLocalDBInstanceName wszSharedInstanceName; + WCHAR wszOwnerSID[MAX_STRING_SID_LENGTH + 1]; + BOOL bIsAutomatic; + } LocalDBInstanceInfo; #pragma pack(pop) -typedef LocalDBInstanceInfo* PLocalDBInstanceInfo; + typedef LocalDBInstanceInfo* PLocalDBInstanceInfo; -// type definition for LocalDBGetInstanceInfo function -typedef HRESULT __cdecl FnLocalDBGetInstanceInfo( - // I the LocalDB instance name - __in_z PCWSTR wszInstanceName, - // O instance information - __out PLocalDBInstanceInfo pInfo, - // I Size of LocalDBInstanceInfo structure in bytes - __in DWORD cbInfo); + // type definition for LocalDBGetInstanceInfo function + typedef HRESULT __cdecl FnLocalDBGetInstanceInfo( + // I the LocalDB instance name + __in_z PCWSTR wszInstanceName, + // O instance information + __out PLocalDBInstanceInfo pInfo, + // I Size of LocalDBInstanceInfo structure in bytes + __in DWORD cbInfo); -// type definition for pointer to LocalDBGetInstances function -typedef FnLocalDBGetInstanceInfo* PFnLocalDBGetInstanceInfo; + // type definition for pointer to LocalDBGetInstances function + typedef FnLocalDBGetInstanceInfo* PFnLocalDBGetInstanceInfo; -// Function: LocalDBGetInstanceInfo -// -// Description: This function returns information about the given instance. -// -// Return values: -// S_OK, if the function succeeds -// -// ERROR_INVALID_PARAMETER, if some of the parameters is invalid -// LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurred. See event log for details -// -FnLocalDBGetInstanceInfo LocalDBGetInstanceInfo; + // Function: LocalDBGetInstanceInfo + // + // Description: This function returns information about the given instance. + // + // Return values: + // S_OK, if the function succeeds + // + // ERROR_INVALID_PARAMETER, if some of the parameters is invalid + // LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurred. See event log for details + // + FnLocalDBGetInstanceInfo LocalDBGetInstanceInfo; -// Version has format: Major.Minor[.Build[.Revision]]. Each of components is 32bit integer which is at most 40 digits and 3 dots -// + // Version has format: Major.Minor[.Build[.Revision]]. Each of components is 32bit integer which is at most 40 digits and 3 dots + // #define MAX_LOCALDB_VERSION_LENGTH 43 -typedef WCHAR TLocalDBVersion[MAX_LOCALDB_VERSION_LENGTH + 1]; -typedef TLocalDBVersion* PTLocalDBVersion; + typedef WCHAR TLocalDBVersion[MAX_LOCALDB_VERSION_LENGTH + 1]; + typedef TLocalDBVersion* PTLocalDBVersion; -// type definition for LocalDBGetVersions function -typedef HRESULT __cdecl FnLocalDBGetVersions( - // O buffer for installed LocalDB versions - __out PTLocalDBVersion pVersions, - // I/O on input has the number slots for versions in the pVersions buffer. On output, - // has the number of existing LocalDB versions - __inout LPDWORD lpdwNumberOfVersions -); + // type definition for LocalDBGetVersions function + typedef HRESULT __cdecl FnLocalDBGetVersions( + // O buffer for installed LocalDB versions + __out PTLocalDBVersion pVersions, + // I/O on input has the number slots for versions in the pVersions buffer. On output, + // has the number of existing LocalDB versions + __inout LPDWORD lpdwNumberOfVersions + ); -// type definition for pointer to LocalDBGetVersions function -typedef FnLocalDBGetVersions* PFnLocalDBGetVersions; + // type definition for pointer to LocalDBGetVersions function + typedef FnLocalDBGetVersions* PFnLocalDBGetVersions; -// Function: LocalDBGetVersions -// -// Description: This function returns all installed LocalDB versions. Returned versions will be in format Major.Minor -// -// Usage Example: -// DWORD dwN = 0; -// LocalDBGetVersions(NULL, &dwN); + // Function: LocalDBGetVersions + // + // Description: This function returns all installed LocalDB versions. Returned versions will be in format Major.Minor + // + // Usage Example: + // DWORD dwN = 0; + // LocalDBGetVersions(NULL, &dwN); -// PTLocalDBVersion versions = (PTLocalDBVersion) malloc(dwN * sizeof(TLocalDBVersion)); -// LocalDBGetVersions(insts, &dwN); + // PTLocalDBVersion versions = (PTLocalDBVersion) malloc(dwN * sizeof(TLocalDBVersion)); + // LocalDBGetVersions(insts, &dwN); -// for (int i = 0; i < dwN; i++) -// wprintf(L"%s\n", insts[i]); -// -// Return values: -// S_OK, if the function succeeds -// -// LOCALDB_ERROR_INSUFFICIENT_BUFFER, the given buffer is to small -// LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurs. -// -FnLocalDBGetVersions LocalDBGetVersions; + // for (int i = 0; i < dwN; i++) + // wprintf(L"%s\n", insts[i]); + // + // Return values: + // S_OK, if the function succeeds + // + // LOCALDB_ERROR_INSUFFICIENT_BUFFER, the given buffer is to small + // LOCALDB_ERROR_INTERNAL_ERROR, if an unexpected error occurs. + // + FnLocalDBGetVersions LocalDBGetVersions; #pragma pack(push) #pragma pack(8) -// DEVNOTE: If you want to modify this structure please read DEVNOTEs on top of function LocalDBGetVersionInfo in sqluserinstance.cpp file. -// -typedef struct _LocalDBVersionInfo -{ - DWORD cbLocalDBVersionInfoSize; - TLocalDBVersion wszVersion; - BOOL bExists; - DWORD dwMajor; - DWORD dwMinor; - DWORD dwBuild; - DWORD dwRevision; -} LocalDBVersionInfo; + // DEVNOTE: If you want to modify this structure please read DEVNOTEs on top of function LocalDBGetVersionInfo in sqluserinstance.cpp file. + // + typedef struct _LocalDBVersionInfo + { + DWORD cbLocalDBVersionInfoSize; + TLocalDBVersion wszVersion; + BOOL bExists; + DWORD dwMajor; + DWORD dwMinor; + DWORD dwBuild; + DWORD dwRevision; + } LocalDBVersionInfo; #pragma pack(pop) -typedef LocalDBVersionInfo* PLocalDBVersionInfo; + typedef LocalDBVersionInfo* PLocalDBVersionInfo; -// type definition for LocalDBGetVersionInfo function -typedef HRESULT __cdecl FnLocalDBGetVersionInfo( - // I LocalDB version string - __in_z PCWSTR wszVersion, - // O version information - __out PLocalDBVersionInfo pVersionInfo, - // I Size of LocalDBVersionInfo structure in bytes - __in DWORD cbVersionInfo -); + // type definition for LocalDBGetVersionInfo function + typedef HRESULT __cdecl FnLocalDBGetVersionInfo( + // I LocalDB version string + __in_z PCWSTR wszVersion, + // O version information + __out PLocalDBVersionInfo pVersionInfo, + // I Size of LocalDBVersionInfo structure in bytes + __in DWORD cbVersionInfo + ); -// type definition for pointer to LocalDBGetVersionInfo function -typedef FnLocalDBGetVersionInfo* PFnLocalDBGetVersionInfo; + // type definition for pointer to LocalDBGetVersionInfo function + typedef FnLocalDBGetVersionInfo* PFnLocalDBGetVersionInfo; -// Function: LocalDBGetVersionInfo -// -// Description: This function returns information about the given LocalDB version -// -// Return values: -// S_OK, if the function succeeds -// LOCALDB_ERROR_INTERNAL_ERROR, if some internal error occurred -// LOCALDB_ERROR_INVALID_PARAMETER, if a input parameter is invalid -// -FnLocalDBGetVersionInfo LocalDBGetVersionInfo; + // Function: LocalDBGetVersionInfo + // + // Description: This function returns information about the given LocalDB version + // + // Return values: + // S_OK, if the function succeeds + // LOCALDB_ERROR_INTERNAL_ERROR, if some internal error occurred + // LOCALDB_ERROR_INVALID_PARAMETER, if a input parameter is invalid + // + FnLocalDBGetVersionInfo LocalDBGetVersionInfo; -typedef HRESULT __cdecl FnLocalDBStartTracing(); -typedef FnLocalDBStartTracing* PFnLocalDBStartTracing; + typedef HRESULT __cdecl FnLocalDBStartTracing(); + typedef FnLocalDBStartTracing* PFnLocalDBStartTracing; -// Function: LocalDBStartTracing -// -// Description: This function will write in registry that Tracing sessions should be started for the current user. -// -// Return values: -// S_OK - on success -// Propper HRESULT in case of failure -// -FnLocalDBStartTracing LocalDBStartTracing; + // Function: LocalDBStartTracing + // + // Description: This function will write in registry that Tracing sessions should be started for the current user. + // + // Return values: + // S_OK - on success + // Propper HRESULT in case of failure + // + FnLocalDBStartTracing LocalDBStartTracing; -typedef HRESULT __cdecl FnLocalDBStopTracing(); -typedef FnLocalDBStopTracing* PFnFnLocalDBStopTracing; + typedef HRESULT __cdecl FnLocalDBStopTracing(); + typedef FnLocalDBStopTracing* PFnFnLocalDBStopTracing; -// Function: LocalDBStopTracing -// -// Description: This function will write in registry that Tracing sessions should be stopped for the current user. -// -// Return values: -// S_OK - on success -// Propper HRESULT in case of failure -// -FnLocalDBStopTracing LocalDBStopTracing; + // Function: LocalDBStopTracing + // + // Description: This function will write in registry that Tracing sessions should be stopped for the current user. + // + // Return values: + // S_OK - on success + // Propper HRESULT in case of failure + // + FnLocalDBStopTracing LocalDBStopTracing; -// type definition for LocalDBShareInstance function -typedef HRESULT __cdecl FnLocalDBShareInstance( - // I the SID of the LocalDB instance owner - __in_opt PSID pOwnerSID, - // I the private name of LocalDB instance which should be shared - __in_z PCWSTR wszPrivateLocalDBInstanceName, - // I the public shared name - __in_z PCWSTR wszSharedName, - // I reserved for the future use. Currently should be set to 0. - __in DWORD dwFlags); + // type definition for LocalDBShareInstance function + typedef HRESULT __cdecl FnLocalDBShareInstance( + // I the SID of the LocalDB instance owner + __in_opt PSID pOwnerSID, + // I the private name of LocalDB instance which should be shared + __in_z PCWSTR wszPrivateLocalDBInstanceName, + // I the public shared name + __in_z PCWSTR wszSharedName, + // I reserved for the future use. Currently should be set to 0. + __in DWORD dwFlags); -// type definition for pointer to LocalDBShareInstance function -typedef FnLocalDBShareInstance* PFnLocalDBShareInstance; + // type definition for pointer to LocalDBShareInstance function + typedef FnLocalDBShareInstance* PFnLocalDBShareInstance; -// Function: LocalDBShareInstance -// -// Description: This function will share the given private instance of the given user with the given shared name. -// This function has to be executed elevated. -// -// Return values: -// HRESULT -// -FnLocalDBShareInstance LocalDBShareInstance; + // Function: LocalDBShareInstance + // + // Description: This function will share the given private instance of the given user with the given shared name. + // This function has to be executed elevated. + // + // Return values: + // HRESULT + // + FnLocalDBShareInstance LocalDBShareInstance; -// type definition for LocalDBUnshareInstance function -typedef HRESULT __cdecl FnLocalDBUnshareInstance( - // I the LocalDB instance name - __in_z PCWSTR pInstanceName, - // I reserved for the future use. Currently should be set to 0. - __in DWORD dwFlags); + // type definition for LocalDBUnshareInstance function + typedef HRESULT __cdecl FnLocalDBUnshareInstance( + // I the LocalDB instance name + __in_z PCWSTR pInstanceName, + // I reserved for the future use. Currently should be set to 0. + __in DWORD dwFlags); -// type definition for pointer to LocalDBUnshareInstance function -typedef FnLocalDBUnshareInstance* PFnLocalDBUnshareInstance; + // type definition for pointer to LocalDBUnshareInstance function + typedef FnLocalDBUnshareInstance* PFnLocalDBUnshareInstance; -// Function: LocalDBUnshareInstance -// -// Description: This function unshares the given LocalDB instance. -// If a shared name is given then that shared instance will be unshared. -// If a private name is given then we will check if the caller -// shares a private instance with the given private name and unshare it. -// -// Return values: -// HRESULT -// -FnLocalDBUnshareInstance LocalDBUnshareInstance; + // Function: LocalDBUnshareInstance + // + // Description: This function unshares the given LocalDB instance. + // If a shared name is given then that shared instance will be unshared. + // If a private name is given then we will check if the caller + // shares a private instance with the given private name and unshare it. + // + // Return values: + // HRESULT + // + FnLocalDBUnshareInstance LocalDBUnshareInstance; #ifdef __cplusplus } // extern "C" #endif #if defined(LOCALDB_DEFINE_PROXY_FUNCTIONS) -//--------------------------------------------------------------------- -// The following section is enabled only if the constant LOCALDB_DEFINE_PROXY_FUNCTIONS -// is defined. It provides an implementation of proxies for each of the LocalDB APIs. -// The proxy implementations use a common function to bind to entry points in the -// latest installed SqlUserInstance DLL, and then forward the requests. -// -// The current implementation loads the SqlUserInstance DLL on the first call into -// a proxy function. There is no provision for unloading the DLL. Note that if the -// process includes multiple binaries (EXE and one or more DLLs), each of them could -// load a separate instance of the SqlUserInstance DLL. -// -// For future consideration: allow the SqlUserInstance DLL to be unloaded dynamically. -// -// WARNING: these functions must not be called in DLL initialization, since a deadlock -// could result loading dependent DLLs. -//--------------------------------------------------------------------- + //--------------------------------------------------------------------- + // The following section is enabled only if the constant LOCALDB_DEFINE_PROXY_FUNCTIONS + // is defined. It provides an implementation of proxies for each of the LocalDB APIs. + // The proxy implementations use a common function to bind to entry points in the + // latest installed SqlUserInstance DLL, and then forward the requests. + // + // The current implementation loads the SqlUserInstance DLL on the first call into + // a proxy function. There is no provision for unloading the DLL. Note that if the + // process includes multiple binaries (EXE and one or more DLLs), each of them could + // load a separate instance of the SqlUserInstance DLL. + // + // For future consideration: allow the SqlUserInstance DLL to be unloaded dynamically. + // + // WARNING: these functions must not be called in DLL initialization, since a deadlock + // could result loading dependent DLLs. + //--------------------------------------------------------------------- -// This macro provides the body for each proxy function. -// + // This macro provides the body for each proxy function. + // #define LOCALDB_PROXY(LocalDBFn) static Fn##LocalDBFn* pfn##LocalDBFn = NULL; if (!pfn##LocalDBFn) {HRESULT hr = LocalDBGetPFn(#LocalDBFn, (FARPROC *)&pfn##LocalDBFn); if (FAILED(hr)) return hr;} return (*pfn##LocalDBFn) -// Structure and function to parse the "Installed Versions" registry subkeys -// + // Structure and function to parse the "Installed Versions" registry subkeys + // typedef struct { DWORD dwComponent[2]; WCHAR wszKeyName[256]; @@ -1508,7 +1606,7 @@ static BOOL ParseVersion(Version * pVersion) if (!fHaveDigit) return FALSE; - pVersion->dwComponent[i] = (DWORD) llVal; + pVersion->dwComponent[i] = (DWORD)llVal; if (*pwch == L'\0') return TRUE; @@ -1538,11 +1636,11 @@ static HRESULT LocalDBGetPFn(LPCSTR szLocalDBFn, FARPROC *pfnLocalDBFn) LONG ec; HKEY hkeyVersions = NULL; HKEY hkeyVersion = NULL; - Version verHigh = {0}; + Version verHigh = { 0 }; Version verCurrent; DWORD cchKeyName; DWORD dwValueType; - WCHAR wszLocalDBDll[MAX_PATH+1]; + WCHAR wszLocalDBDll[MAX_PATH + 1]; DWORD cbLocalDBDll = sizeof(wszLocalDBDll) - sizeof(WCHAR); // to deal with RegQueryValueEx null-termination quirk HMODULE hLocalDBDllTemp = NULL; @@ -1592,7 +1690,7 @@ static HRESULT LocalDBGetPFn(LPCSTR szLocalDBFn, FARPROC *pfnLocalDBFn) { goto Cleanup; } - if (ERROR_SUCCESS != (ec = RegQueryValueExW(hkeyVersion, L"InstanceAPIPath", NULL, &dwValueType, (PBYTE) wszLocalDBDll, &cbLocalDBDll))) + if (ERROR_SUCCESS != (ec = RegQueryValueExW(hkeyVersion, L"InstanceAPIPath", NULL, &dwValueType, (PBYTE)wszLocalDBDll, &cbLocalDBDll))) { goto Cleanup; } @@ -1604,7 +1702,7 @@ static HRESULT LocalDBGetPFn(LPCSTR szLocalDBFn, FARPROC *pfnLocalDBFn) // Ensure string value null-terminated // Note that we left a spare character in the output buffer for RegQueryValueEx for this purpose // - wszLocalDBDll[cbLocalDBDll/sizeof(WCHAR)] = L'\0'; + wszLocalDBDll[cbLocalDBDll / sizeof(WCHAR)] = L'\0'; hLocalDBDllTemp = LoadLibraryW(wszLocalDBDll); if (NULL == hLocalDBDllTemp) @@ -1619,7 +1717,7 @@ static HRESULT LocalDBGetPFn(LPCSTR szLocalDBFn, FARPROC *pfnLocalDBFn) hLocalDBDllTemp = NULL; } ec = ERROR_SUCCESS; -Cleanup: + Cleanup: if (hLocalDBDllTemp) FreeLibrary(hLocalDBDllTemp); if (hkeyVersion) @@ -1641,7 +1739,7 @@ Cleanup: if (!pfn) { - return HRESULT_FROM_WIN32(GetLastError()); + return HRESULT_FROM_WIN32(GetLastError()); } *pfnLocalDBFn = pfn; return S_OK; @@ -1651,165 +1749,165 @@ Cleanup: // HRESULT __cdecl -LocalDBCreateInstance ( - // I the LocalDB version (e.g. 11.0 or 11.0.1094.2) - __in_z PCWSTR wszVersion, - // I the instance name - __in_z PCWSTR pInstanceName, - // I reserved for the future use. Currently should be set to 0. - __in DWORD dwFlags -) +LocalDBCreateInstance( + // I the LocalDB version (e.g. 11.0 or 11.0.1094.2) + __in_z PCWSTR wszVersion, + // I the instance name + __in_z PCWSTR pInstanceName, + // I reserved for the future use. Currently should be set to 0. + __in DWORD dwFlags + ) { - LOCALDB_PROXY(LocalDBCreateInstance)(wszVersion, pInstanceName, dwFlags); + LOCALDB_PROXY(LocalDBCreateInstance)(wszVersion, pInstanceName, dwFlags); } HRESULT __cdecl LocalDBStartInstance( - // I the instance name - __in_z PCWSTR pInstanceName, - // I reserved for the future use. Currently should be set to 0. - __in DWORD dwFlags, - // O the buffer to store the connection string to the LocalDB instance - __out_ecount_z_opt(*lpcchSqlConnection) LPWSTR wszSqlConnection, - // I/O on input has the size of the wszSqlConnection buffer in characters. On output, if the given buffer size is - // too small, has the buffer size required, in characters, including trailing null. - __inout_opt LPDWORD lpcchSqlConnection -) + // I the instance name + __in_z PCWSTR pInstanceName, + // I reserved for the future use. Currently should be set to 0. + __in DWORD dwFlags, + // O the buffer to store the connection string to the LocalDB instance + __out_ecount_z_opt(*lpcchSqlConnection) LPWSTR wszSqlConnection, + // I/O on input has the size of the wszSqlConnection buffer in characters. On output, if the given buffer size is + // too small, has the buffer size required, in characters, including trailing null. + __inout_opt LPDWORD lpcchSqlConnection + ) { - LOCALDB_PROXY(LocalDBStartInstance)(pInstanceName, dwFlags, wszSqlConnection, lpcchSqlConnection); + LOCALDB_PROXY(LocalDBStartInstance)(pInstanceName, dwFlags, wszSqlConnection, lpcchSqlConnection); } HRESULT __cdecl -LocalDBStopInstance ( - // I the instance name - __in_z PCWSTR pInstanceName, - // I Available flags: - // LOCALDB_SHUTDOWN_KILL_PROCESS - force the instance to stop immediately - // LOCALDB_SHUTDOWN_WITH_NOWAIT - shutdown the instance with NOWAIT option - __in DWORD dwFlags, - // I the time in seconds to wait this operation to complete. If this value is 0, this function will return immediately - // without waiting for LocalDB instance to stop - __in ULONG ulTimeout -) +LocalDBStopInstance( + // I the instance name + __in_z PCWSTR pInstanceName, + // I Available flags: + // LOCALDB_SHUTDOWN_KILL_PROCESS - force the instance to stop immediately + // LOCALDB_SHUTDOWN_WITH_NOWAIT - shutdown the instance with NOWAIT option + __in DWORD dwFlags, + // I the time in seconds to wait this operation to complete. If this value is 0, this function will return immediately + // without waiting for LocalDB instance to stop + __in ULONG ulTimeout + ) { - LOCALDB_PROXY(LocalDBStopInstance)(pInstanceName, dwFlags, ulTimeout); + LOCALDB_PROXY(LocalDBStopInstance)(pInstanceName, dwFlags, ulTimeout); } HRESULT __cdecl -LocalDBDeleteInstance ( - // I the instance name - __in_z PCWSTR pInstanceName, - // reserved for the future use. Currently should be set to 0. - __in DWORD dwFlags -) +LocalDBDeleteInstance( + // I the instance name + __in_z PCWSTR pInstanceName, + // reserved for the future use. Currently should be set to 0. + __in DWORD dwFlags + ) { - LOCALDB_PROXY(LocalDBDeleteInstance)(pInstanceName, dwFlags); + LOCALDB_PROXY(LocalDBDeleteInstance)(pInstanceName, dwFlags); } HRESULT __cdecl LocalDBFormatMessage( - // I the LocalDB error code - __in HRESULT hrLocalDB, - // I Available flags: - // LOCALDB_TRUNCATE_ERR_MESSAGE - if the input buffer is too short, - // the error message will be truncated to fit into the buffer - __in DWORD dwFlags, - // I Language desired (LCID) or 0 (in which case Win32 FormatMessage order is used) - __in DWORD dwLanguageId, - // O the buffer to store the LocalDB error message - __out_ecount_z(*lpcchMessage) LPWSTR wszMessage, - // I/O on input has the size of the wszMessage buffer in characters. On output, if the given buffer size is - // too small, has the buffer size required, in characters, including trailing null. If the function succeeds - // contains the number of characters in the message, excluding the trailing null - __inout LPDWORD lpcchMessage -) + // I the LocalDB error code + __in HRESULT hrLocalDB, + // I Available flags: + // LOCALDB_TRUNCATE_ERR_MESSAGE - if the input buffer is too short, + // the error message will be truncated to fit into the buffer + __in DWORD dwFlags, + // I Language desired (LCID) or 0 (in which case Win32 FormatMessage order is used) + __in DWORD dwLanguageId, + // O the buffer to store the LocalDB error message + __out_ecount_z(*lpcchMessage) LPWSTR wszMessage, + // I/O on input has the size of the wszMessage buffer in characters. On output, if the given buffer size is + // too small, has the buffer size required, in characters, including trailing null. If the function succeeds + // contains the number of characters in the message, excluding the trailing null + __inout LPDWORD lpcchMessage + ) { - LOCALDB_PROXY(LocalDBFormatMessage)(hrLocalDB, dwFlags, dwLanguageId, wszMessage, lpcchMessage); + LOCALDB_PROXY(LocalDBFormatMessage)(hrLocalDB, dwFlags, dwLanguageId, wszMessage, lpcchMessage); } HRESULT __cdecl LocalDBGetInstances( - // O buffer with instance names - __out PTLocalDBInstanceName pInstanceNames, - // I/O on input has the number slots for instance names in the pInstanceNames buffer. On output, - // has the number of existing LocalDB instances - __inout LPDWORD lpdwNumberOfInstances -) + // O buffer with instance names + __out PTLocalDBInstanceName pInstanceNames, + // I/O on input has the number slots for instance names in the pInstanceNames buffer. On output, + // has the number of existing LocalDB instances + __inout LPDWORD lpdwNumberOfInstances + ) { - LOCALDB_PROXY(LocalDBGetInstances)(pInstanceNames, lpdwNumberOfInstances); + LOCALDB_PROXY(LocalDBGetInstances)(pInstanceNames, lpdwNumberOfInstances); } HRESULT __cdecl LocalDBGetInstanceInfo( - // I the instance name - __in_z PCWSTR wszInstanceName, - // O instance information - __out PLocalDBInstanceInfo pInfo, - // I Size of LocalDBInstanceInfo structure in bytes - __in DWORD cbInfo -) + // I the instance name + __in_z PCWSTR wszInstanceName, + // O instance information + __out PLocalDBInstanceInfo pInfo, + // I Size of LocalDBInstanceInfo structure in bytes + __in DWORD cbInfo + ) { - LOCALDB_PROXY(LocalDBGetInstanceInfo)(wszInstanceName, pInfo, cbInfo); + LOCALDB_PROXY(LocalDBGetInstanceInfo)(wszInstanceName, pInfo, cbInfo); } HRESULT __cdecl LocalDBStartTracing() { - LOCALDB_PROXY(LocalDBStartTracing)(); + LOCALDB_PROXY(LocalDBStartTracing)(); } HRESULT __cdecl LocalDBStopTracing() { - LOCALDB_PROXY(LocalDBStopTracing)(); + LOCALDB_PROXY(LocalDBStopTracing)(); } -HRESULT __cdecl +HRESULT __cdecl LocalDBShareInstance( - // I the SID of the LocalDB instance owner - __in_opt PSID pOwnerSID, - // I the private name of LocalDB instance which should be shared - __in_z PCWSTR wszLocalDBInstancePrivateName, - // I the public shared name - __in_z PCWSTR wszSharedName, - // I reserved for the future use. Currently should be set to 0. - __in DWORD dwFlags) + // I the SID of the LocalDB instance owner + __in_opt PSID pOwnerSID, + // I the private name of LocalDB instance which should be shared + __in_z PCWSTR wszLocalDBInstancePrivateName, + // I the public shared name + __in_z PCWSTR wszSharedName, + // I reserved for the future use. Currently should be set to 0. + __in DWORD dwFlags) { - LOCALDB_PROXY(LocalDBShareInstance)(pOwnerSID, wszLocalDBInstancePrivateName, wszSharedName, dwFlags); + LOCALDB_PROXY(LocalDBShareInstance)(pOwnerSID, wszLocalDBInstancePrivateName, wszSharedName, dwFlags); } HRESULT __cdecl LocalDBGetVersions( - // O buffer for installed LocalDB versions - __out PTLocalDBVersion pVersions, - // I/O on input has the number slots for versions in the pVersions buffer. On output, - // has the number of existing LocalDB versions - __inout LPDWORD lpdwNumberOfVersions -) + // O buffer for installed LocalDB versions + __out PTLocalDBVersion pVersions, + // I/O on input has the number slots for versions in the pVersions buffer. On output, + // has the number of existing LocalDB versions + __inout LPDWORD lpdwNumberOfVersions + ) { - LOCALDB_PROXY(LocalDBGetVersions)(pVersions, lpdwNumberOfVersions); + LOCALDB_PROXY(LocalDBGetVersions)(pVersions, lpdwNumberOfVersions); } HRESULT __cdecl LocalDBUnshareInstance( - // I the LocalDB instance name - __in_z PCWSTR pInstanceName, - // I reserved for the future use. Currently should be set to 0. - __in DWORD dwFlags) + // I the LocalDB instance name + __in_z PCWSTR pInstanceName, + // I reserved for the future use. Currently should be set to 0. + __in DWORD dwFlags) { - LOCALDB_PROXY(LocalDBUnshareInstance)(pInstanceName, dwFlags); + LOCALDB_PROXY(LocalDBUnshareInstance)(pInstanceName, dwFlags); } -HRESULT __cdecl +HRESULT __cdecl LocalDBGetVersionInfo( - // I LocalDB version string - __in_z PCWSTR wszVersion, - // O version information - __out PLocalDBVersionInfo pVersionInfo, - // I Size of LocalDBVersionInfo structure in bytes - __in DWORD cbVersionInfo) + // I LocalDB version string + __in_z PCWSTR wszVersion, + // O version information + __out PLocalDBVersionInfo pVersionInfo, + // I Size of LocalDBVersionInfo structure in bytes + __in DWORD cbVersionInfo) { - LOCALDB_PROXY(LocalDBGetVersionInfo)(wszVersion, pVersionInfo, cbVersionInfo); + LOCALDB_PROXY(LocalDBGetVersionInfo)(wszVersion, pVersionInfo, cbVersionInfo); } #endif @@ -2173,7 +2271,7 @@ LocalDBGetVersionInfo( // #define LOCALDB_ERROR_CANNOT_LOAD_RESOURCES ((HRESULT)0x89C50121L) - // Detailed error descriptions +// Detailed error descriptions // // MessageId: LOCALDB_EDETAIL_DATADIRECTORY_IS_MISSING // diff --git a/sqlsrv/php_sqlsrv.h b/sqlsrv/php_sqlsrv.h index b69b138a..a773c474 100644 --- a/sqlsrv/php_sqlsrv.h +++ b/sqlsrv/php_sqlsrv.h @@ -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, ... ) \ diff --git a/sqlsrv/stmt.cpp b/sqlsrv/stmt.cpp index 9d482172..c4f51d9d 100644 --- a/sqlsrv/stmt.cpp +++ b/sqlsrv/stmt.cpp @@ -88,9 +88,10 @@ 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 - TSRMLS_DC ); +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 ); sqlsrv_phptype determine_sqlsrv_php_type( sqlsrv_stmt const* stmt, SQLINTEGER sql_type, SQLUINTEGER size, bool prefer_string ); @@ -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( 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( 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( ¶ms_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( ¶ms_m[i], value_z ); zr = ( NULL != ¶ms_m[i] ) ? SUCCESS : FAILURE; - CHECK_ZEND_ERROR( zr, stmt, SS_SQLSRV_ERROR_ZEND_OBJECT_FAILED, class_name ) { - throw ss::SSException(); - } - } //for + CHECK_ZEND_ERROR( zr, stmt, SS_SQLSRV_ERROR_ZEND_OBJECT_FAILED, class_name ) { + throw ss::SSException(); + } + 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(return_value); + fci.object = reinterpret_cast( &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(return_value); + fci.object = reinterpret_cast( &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; + zval* value_z = NULL; - 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); - - CHECK_CUSTOM_ERROR( type != HASH_KEY_IS_LONG, stmt, SS_SQLSRV_ERROR_PARAM_INVALID_INDEX ) { - throw ss::SSException(); - } + // make sure it's an integer 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 @@ -1190,21 +1194,21 @@ void mark_params_by_reference( ss_sqlsrv_stmt* stmt, zval* params_z TSRMLS_DC ) // parameter array's first element. // if it's a sole variable - if (Z_TYPE_P(value_z) != IS_ARRAY) { - ZVAL_MAKE_REF(value_z); + if ( Z_TYPE_P( value_z ) != IS_ARRAY ) { + ZVAL_MAKE_REF( value_z ); } else { zval* var = NULL; - int zr = (NULL != (var = zend_hash_index_find(Z_ARRVAL_P(value_z), 0))) ? SUCCESS : FAILURE; - CHECK_CUSTOM_ERROR(zr == FAILURE, stmt, SS_SQLSRV_ERROR_VAR_REQUIRED, index + 1) { + int zr = ( NULL != ( var = zend_hash_index_find( Z_ARRVAL_P( value_z ), 0 ))) ? SUCCESS : FAILURE; + CHECK_CUSTOM_ERROR( zr == FAILURE, stmt, SS_SQLSRV_ERROR_VAR_REQUIRED, index + 1 ) { throw ss::SSException(); } - ZVAL_MAKE_REF(var); + ZVAL_MAKE_REF( var ); } - } + } ZEND_HASH_FOREACH_END(); // save our parameters for later. - Z_TRY_ADDREF_P(params_z); + Z_TRY_ADDREF_P( params_z ); stmt->params_z = params_z; } @@ -1225,38 +1229,33 @@ 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_ulong index = -1; + zend_string *key = NULL; + zval* param_z = NULL; - zend_string *key = NULL; - zend_ulong index = -1; - zval* param_z = NULL; - zval* value_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(); - if( stmt->encoding() == SQLSRV_ENCODING_DEFAULT ) { - encoding = stmt->conn->encoding(); - } - SQLSMALLINT sql_type = SQL_UNKNOWN_TYPE; - SQLULEN column_size = SQLSRV_UNKNOWN_SIZE; - SQLSMALLINT decimal_digits = 0; - 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); + SQLSRV_ENCODING encoding = stmt->encoding(); + if( stmt->encoding() == SQLSRV_ENCODING_DEFAULT ) { + encoding = stmt->conn->encoding(); + } + SQLSMALLINT sql_type = SQL_UNKNOWN_TYPE; + SQLULEN column_size = SQLSRV_UNKNOWN_SIZE; + SQLSMALLINT decimal_digits = 0; + SQLSRV_PHPTYPE php_out_type = SQLSRV_PHPTYPE_INVALID; - 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 ); + // make sure it's an integer 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(); + } // if it's a parameter array if( Z_TYPE_P( param_z ) == IS_ARRAY ) { zval* var = NULL; - int zr = (NULL != (var = zend_hash_index_find(Z_ARRVAL_P(param_z), 0))) ? SUCCESS : FAILURE; + int zr = ( NULL != ( var = zend_hash_index_find( Z_ARRVAL_P( param_z ), 0 ))) ? SUCCESS : FAILURE; CHECK_CUSTOM_ERROR( zr == FAILURE, stmt, SS_SQLSRV_ERROR_VAR_REQUIRED, index + 1 ) { throw ss::SSException(); } @@ -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 ); @@ -1481,69 +1480,60 @@ 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) { + if ( in_val == NULL ) { + ZVAL_NULL( &out_zval); + return; + } - out_zval = ( zval* )sqlsrv_malloc(sizeof( zval )); - ZVAL_NULL( out_zval ); - return out_zval; - } + switch (sqlsrv_php_type) { - switch( sqlsrv_php_type ) { - - 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( in_val ))); - } - else { - ZVAL_DOUBLE( out_zval, *( static_cast( in_val ))); - } - sqlsrv_free( in_val ); - break; - } + case SQLSRV_PHPTYPE_INT: + case SQLSRV_PHPTYPE_FLOAT: + { + if (sqlsrv_php_type == SQLSRV_PHPTYPE_INT) { + ZVAL_LONG( &out_zval, *(static_cast( in_val ))); + } + else { + ZVAL_DOUBLE( &out_zval, *(static_cast( 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( in_val ), field_len ); - sqlsrv_free( in_val ); - break; - } - - case SQLSRV_PHPTYPE_STREAM: - case SQLSRV_PHPTYPE_DATETIME : - { - out_zval = ( static_cast( in_val ) ); + case SQLSRV_PHPTYPE_STRING: + { + ZVAL_STRINGL( &out_zval, static_cast( in_val ), field_len); + break; + } - //addref here because deleting out_zval later will decrement the refcount - Z_TRY_ADDREF_P( out_zval ); - in_val = NULL; - break; - } - - case SQLSRV_PHPTYPE_NULL: - out_zval = ( zval* )sqlsrv_malloc(sizeof( zval )); - ZVAL_NULL( out_zval ); - break; + case SQLSRV_PHPTYPE_STREAM: + { + out_zval = *( static_cast( 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( in_val )); + break; + } - default: - DIE( "Unknown php type" ); - break; - } + case SQLSRV_PHPTYPE_NULL: + ZVAL_NULL(&out_zval); + break; - return out_zval; + default: + DIE("Unknown php type"); + break; + } + 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,94 +1785,92 @@ 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 - TSRMLS_DC ) +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 ); + 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; - // 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() ) { - throw ss::SSException(); - } + // 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()) { + throw ss::SSException(); + } - // get the numer of columns in the result set - SQLSMALLINT num_cols = core::SQLNumResultCols( stmt TSRMLS_CC ); - - // if this is the first fetch in a new result set, then get the field names and - // store them off for successive fetches. - if(( fetch_type & SQLSRV_FETCH_ASSOC ) && stmt->fetch_field_names == NULL ) { + // get the numer of columns in the result set + SQLSMALLINT num_cols = core::SQLNumResultCols(stmt TSRMLS_CC); - SQLSMALLINT field_name_len; - char field_name_temp[ SS_MAXCOLNAMELEN+1 ]; - sqlsrv_malloc_auto_ptr field_names; - field_names = static_cast( sqlsrv_malloc( num_cols * sizeof( sqlsrv_fetch_field_name ))); + // if this is the first fetch in a new result set, then get the field names and + // store them off for successive fetches. + if(( fetch_type & SQLSRV_FETCH_ASSOC ) && stmt->fetch_field_names == NULL) { - for( int i = 0; i < num_cols; ++i ) { + SQLSMALLINT field_name_len; + char field_name_temp[SS_MAXCOLNAMELEN + 1]; + sqlsrv_malloc_auto_ptr field_names; + field_names = static_cast( sqlsrv_malloc( num_cols * sizeof(sqlsrv_fetch_field_name))); - core::SQLColAttribute( stmt, i + 1, SQL_DESC_NAME, field_name_temp, SS_MAXCOLNAMELEN+1, &field_name_len, NULL - TSRMLS_CC ); - field_names[ i ].name = static_cast( sqlsrv_malloc( field_name_len, sizeof( char ), 1 )); - memcpy( (void*) field_names[ i ].name, field_name_temp, field_name_len ); - field_names[ i ].name[ field_name_len ] = '\0'; // null terminate the field name since SQLColAttribute doesn't. - field_names[ i ].len = field_name_len + 1; - } + for(int i = 0; i < num_cols; ++i) { - stmt->fetch_field_names = field_names; - stmt->fetch_fields_count = num_cols; - field_names.transferred(); - } + core::SQLColAttribute(stmt, i + 1, SQL_DESC_NAME, field_name_temp, SS_MAXCOLNAMELEN + 1, &field_name_len, NULL + TSRMLS_CC); + field_names[i].name = static_cast( sqlsrv_malloc( field_name_len, sizeof(char), 1 )); + memcpy(( void* )field_names[i].name, field_name_temp, field_name_len); + field_names[i].name[field_name_len] = '\0'; // null terminate the field name since SQLColAttribute doesn't. + field_names[i].len = field_name_len + 1; + } - int zr = array_init( &fields ); - CHECK_ZEND_ERROR( zr, stmt, SQLSRV_ERROR_ZEND_HASH ) { - throw ss::SSException(); - } + stmt->fetch_field_names = field_names; + stmt->fetch_fields_count = num_cols; + field_names.transferred(); + } - // get the fields - for( int i = 0; i < num_cols; ++i ) { + int zr = array_init( &fields ); + CHECK_ZEND_ERROR( zr, stmt, SQLSRV_ERROR_ZEND_HASH ) { + throw ss::SSException(); + } + for( int i = 0; i < num_cols; ++i ) { + SQLLEN field_len = -1; - 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 ); - 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 )); - - 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); - } + 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 ) { - if( fetch_type & SQLSRV_FETCH_ASSOC ) { - - CHECK_CUSTOM_WARNING_AS_ERROR(( stmt->fetch_field_names[ i ].len == 1 && !allow_empty_field_names ), stmt, - SS_SQLSRV_WARNING_FIELD_NAME_EMPTY ) { - throw ss::SSException(); - } + zr = add_next_index_zval( &fields, &field ); + CHECK_ZEND_ERROR( zr, stmt, SQLSRV_ERROR_ZEND_HASH ) { + throw ss::SSException(); + } + } - if( stmt->fetch_field_names[ i ].len > 1 || allow_empty_field_names ) { - - zr = add_assoc_zval( &fields, stmt->fetch_field_names[ i ].name, &field ); - CHECK_ZEND_ERROR( zr, stmt, SQLSRV_ERROR_ZEND_HASH ) { - throw ss::SSException(); - } - Z_TRY_ADDREF_P(&field); - } - } - } //for loop + if( fetch_type & SQLSRV_FETCH_ASSOC ) { + + CHECK_CUSTOM_WARNING_AS_ERROR(( stmt->fetch_field_names[i].len == 1 && !allow_empty_field_names ), stmt, + SS_SQLSRV_WARNING_FIELD_NAME_EMPTY) { + throw ss::SSException(); + } + + if( stmt->fetch_field_names[ i ].len > 1 || allow_empty_field_names ) { + + zr = add_assoc_zval( &fields, stmt->fetch_field_names[i].name, &field ); + CHECK_ZEND_ERROR( zr, stmt, SQLSRV_ERROR_ZEND_HASH ) { + throw ss::SSException(); + } + } + } + //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(zend_hash_get_current_data_ptr(g_ss_encodings_ht))) != NULL ? SUCCESS : FAILURE; - if( zr == FAILURE ) { - DIE( "Fatal: Error retrieving encoding from encoding hash table." ); - } - - if( !stricmp( encoding_string, encoding->iana )) { - phptype_encoding.typeinfo.encoding = encoding->code_page; - return true; - } - } + 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( 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; } diff --git a/sqlsrv/template.rc b/sqlsrv/template.rc index 0951e7d8..fd6ea640 100644 --- a/sqlsrv/template.rc +++ b/sqlsrv/template.rc @@ -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" diff --git a/sqlsrv/util.cpp b/sqlsrv/util.cpp index fb4c4ca3..a06e8e01 100644 --- a/sqlsrv/util.cpp +++ b/sqlsrv/util.cpp @@ -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; + } + int result; + 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 )) { - LOG(SEV_ERROR, "An invalid parameter was passed to %1!s!.", _FN_ ); - RETURN_FALSE; - } + RETURN_FALSE; + } + } + 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 )) { - if( flags == SQLSRV_ERR_ALL ) { - - int result; - zval both_z; - ZVAL_UNDEF( &both_z ); - result = array_init( &both_z ); - if( result == FAILURE ) { + RETURN_FALSE; + } + } + if( zend_hash_num_elements( Z_ARRVAL_P( &err_z )) == 0 ) { - RETURN_FALSE; - } - - if( Z_TYPE_P( SQLSRV_G( errors )) == IS_ARRAY && !sqlsrv_merge_zend_hash( &both_z, SQLSRV_G( errors ) 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 ) { - - 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_NULL(); + } + RETURN_ZVAL( &err_z, 1, 1 ); } // sqlsrv_configure( string $setting, mixed $value ) @@ -875,20 +861,21 @@ 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(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( error_temp ); if (NULL == error) { return false; } - if( !strncmp( reinterpret_cast( error->sqlstate ), sql_state, SQL_SQLSTATE_SIZE ) && - ( error->native_code == native_code || error->native_code == -1 )) { - return true; - } - } + if( !strncmp( reinterpret_cast( error->sqlstate ), sql_state, SQL_SQLSTATE_SIZE ) && + ( 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; + zend_ulong index = -1; + zend_string* key = NULL; + zval* value_z = NULL; - for( zend_hash_internal_pointer_reset( src_ht ); - zend_hash_has_more_elements( src_ht ) == SUCCESS; - zend_hash_move_forward( src_ht ) ) { - - zval* value_z = NULL; - result = (value_z = zend_hash_get_current_data( src_ht )) != NULL ? SUCCESS : FAILURE; - if( result == FAILURE ) { - 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 ); + 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; + } - 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); - } + 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; }