RC release code

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

View file

@ -42,7 +42,7 @@ const int INFO_BUFFER_LEN = 256;
const char* PROCESSOR_ARCH[] = { "x86", "x64", "ia64" };
// ODBC driver name.
const char CONNECTION_STRING_DRIVER_NAME[] = "Driver={ODBC Driver 11 for SQL Server};";
const char* CONNECTION_STRING_DRIVER_NAME[] = {"Driver={ODBC Driver 13 for SQL Server};", "Driver={ODBC Driver 11 for SQL Server};"};
// default options if only the server is specified
const char CONNECTION_STRING_DEFAULT_OPTIONS[] = "Mars_Connection={Yes}";
@ -124,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<unsigned int>( 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<unsigned int>( conn_str.length() + 1 ) * sizeof( wchar_t );
wconn_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_UTF8, conn_str.c_str(), static_cast<unsigned int>( 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<SQLWCHAR*>( wconn_string.get() ),
static_cast<SQLSMALLINT>( 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<char*>( 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<unsigned int>(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<SQLWCHAR*>( wconn_string.get()),
static_cast<SQLSMALLINT>( 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<char*>( 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<DRIVER_VERSION>( 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<char*>( 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";

View file

@ -970,6 +970,14 @@ enum SERVER_VERSION {
SERVER_VERSION_2008, // use this for anything 2008 or later
};
// supported driver versions.
enum DRIVER_VERSION : size_t {
MIN = 0,
ODBC_DRIVER_13 = MIN,
ODBC_DRIVER_11 = 1,
MAX = ODBC_DRIVER_11,
};
// forward decl
struct sqlsrv_stmt;
struct stmt_option;
@ -981,6 +989,8 @@ struct sqlsrv_conn : public sqlsrv_context {
// instance variables
SERVER_VERSION server_version; // version of the server that we're connected to
DRIVER_VERSION driver_version;
// initialize with default values
sqlsrv_conn( SQLHANDLE h, error_callback e, void* drv, SQLSRV_ENCODING encoding TSRMLS_DC ) :
sqlsrv_context( h, SQL_HANDLE_DBC, e, drv, encoding )
@ -1258,7 +1268,7 @@ struct sqlsrv_stmt : public sqlsrv_context {
unsigned int current_stream_read; // # of bytes read so far. (if we read an empty PHP stream, we send an empty string
// to the server)
zval field_cache; // cache for a single row of fields, to allow multiple and out of order retrievals
zval* active_stream; // the currently active stream reading data from the database
zval active_stream; // the currently active stream reading data from the database
sqlsrv_stmt( sqlsrv_conn* c, SQLHANDLE handle, error_callback e, void* drv TSRMLS_DC );
virtual ~sqlsrv_stmt( void );
@ -1313,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<const char*>( err_msg ), CONNECTION_BUSY_ODBC_ERROR )) {
std::size_t driver_version = stmt->conn->driver_version;
if(( len == sizeof( CONNECTION_BUSY_ODBC_ERROR[driver_version] ) - 1 ) &&
!strcmp( reinterpret_cast<const char*>( err_msg ), CONNECTION_BUSY_ODBC_ERROR[driver_version] )) {
THROW_CORE_ERROR( stmt, SQLSRV_ERROR_MARS_OFF );
}

View file

@ -81,7 +81,7 @@ size_t calc_utf8_missing( sqlsrv_stmt* stmt, const char* buffer, size_t buffer_e
bool check_for_next_stream_parameter( sqlsrv_stmt* stmt TSRMLS_DC );
bool convert_input_param_to_utf16( zval* input_param_z, zval* convert_param_z );
void core_get_field_common(__inout sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_phptype
sqlsrv_php_type, __out void** field_value, __out SQLLEN* field_len TSRMLS_DC );
sqlsrv_php_type, __out void*& field_value, __out SQLLEN* field_len TSRMLS_DC);
// returns the ODBC C type constant that matches the PHP type and encoding given
SQLSMALLINT default_c_type( sqlsrv_stmt* stmt, SQLULEN paramno, zval const* param_z, SQLSRV_ENCODING encoding TSRMLS_DC );
void default_sql_size_and_scale( sqlsrv_stmt* stmt, unsigned int paramno, zval* param_z, SQLSRV_ENCODING encoding,
@ -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, &param_input_strings TSRMLS_CC );
@ -152,7 +152,7 @@ sqlsrv_stmt::sqlsrv_stmt( sqlsrv_conn* c, SQLHANDLE handle, error_callback e, vo
// desctructor for sqlsrv statement.
sqlsrv_stmt::~sqlsrv_stmt( void )
{
if( active_stream ) {
if( Z_TYPE( active_stream ) != IS_UNDEF ) {
TSRMLS_FETCH();
close_active_stream( this TSRMLS_CC );
}
@ -248,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<wchar_t> wsql_string;
@ -705,8 +699,9 @@ void core_sqlsrv_execute( sqlsrv_stmt* stmt TSRMLS_DC, const char* sql, int sql_
finalize_output_parameters( stmt TSRMLS_CC );
}
// stream parameters are sent, clean the Hashtable
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<field_cache*>(zend_hash_index_find_ptr(Z_ARRVAL(stmt->field_cache), static_cast<zend_ulong>(field_index))))) {
// the field value is NULL
if( cached->value == NULL ) {
*field_value = NULL;
*field_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<char*>( *field_value )[ cached->len ] = '\0';
}
*field_len = cached->len;
if( sqlsrv_php_type_out ) { *sqlsrv_php_type_out = static_cast<SQLSRV_PHPTYPE>( cached->type.typeinfo.type ); }
}
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<field_cache*>( zend_hash_index_find_ptr( Z_ARRVAL( stmt->field_cache ), static_cast<zend_ulong>( field_index ))))) {
// the field value is NULL
if( cached->value == NULL ) {
field_value = NULL;
*field_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<field_cache*>(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<char*>( field_value )[ cached->len ] = '\0';
}
*field_len = cached->len;
if( sqlsrv_php_type_out) { *sqlsrv_php_type_out = static_cast<SQLSRV_PHPTYPE>(cached->type.typeinfo.type); }
}
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<SQLINTEGER>( sql_field_type ), static_cast<SQLUINTEGER>( 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_PHPTYPE>( 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<field_cache*>(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<SQLINTEGER>( sql_field_type ), static_cast<SQLUINTEGER>( 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_PHPTYPE>( 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<long> field_value_temp;
field_value_temp = static_cast<long*>( 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<long> field_value_temp;
field_value_temp = static_cast<long*>( 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<double> field_value_temp;
field_value_temp = static_cast<double*>( 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<double> field_value_temp;
field_value_temp = static_cast<double*>( 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<void*>( 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<void*>( 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<sqlsrv_stream*>( stream->abstract );
ss->stmt = stmt;
ss->field_index = field_index;
ss->sql_type = static_cast<SQLUSMALLINT>( sql_type );
ss->encoding = static_cast<SQLSRV_ENCODING>( 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<void*>( 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<void*>( 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<void*>( return_value_z.get());
return_value_z.transferred();
zend_string_free( Z_STR( field_value_temp_z ));
zend_string_free( Z_STR( function_z ));
break;
}
// 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<sqlsrv_stream*>( stream->abstract );
ss->stmt = stmt;
ss->field_index = field_index;
ss->sql_type = static_cast<SQLUSMALLINT>( sql_type );
ss->encoding = static_cast<SQLSRV_ENCODING>( 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<void*>( 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<sqlsrv_output_param*>( output_param_temp );
zval* value_z = Z_REFVAL_P( output_param->param_z );
switch( Z_TYPE_P( value_z )) {
case IS_STRING:
@ -2037,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<char*>( 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<char*>( 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<char*>( 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<char*>( 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<char*>( 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<char*>( sqlsrv_realloc( field_value_temp, field_len_temp + extra + 1 ));
bool converted = convert_string_from_utf16_inplace( static_cast<SQLSRV_ENCODING>( 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<char*>( 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_ENCODING>( 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_ENCODING>( 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<char*>( 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_ENCODING>( 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;
}
}

View file

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

View file

@ -463,6 +463,8 @@ int pdo_sqlsrv_db_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC)
dbh->error_mode = prev_err_mode; // reset the error mode
g_henv_cp->invalidate();
return 0;
}
catch( ... ) {
@ -1310,25 +1312,20 @@ void validate_stmt_options( sqlsrv_context& ctx, zval* stmt_options, __inout Has
if( stmt_options ) {
HashTable* options_ht = Z_ARRVAL_P( stmt_options );
for( zend_hash_internal_pointer_reset( options_ht ); zend_hash_has_more_elements( options_ht ) == SUCCESS;
zend_hash_move_forward( options_ht )) {
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& ) {

View file

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

View file

@ -57,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<sqlsrv_encoding*>( 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<SQLSRV_ENCODING>( ss_encoding->code_page ));
return;
}
}
conn->set_encoding( static_cast<SQLSRV_ENCODING>(ss_encoding->code_page ));
return;
}
} ZEND_HASH_FOREACH_END();
THROW_SS_ERROR( conn, SS_SQLSRV_ERROR_CONNECT_ILLEGAL_ENCODING, encoding );
}
@ -1062,8 +1062,7 @@ PHP_FUNCTION( sqlsrv_query )
void free_stmt_resource( zval* stmt_z TSRMLS_DC )
{
int zr = zend_list_close(Z_RES_P(stmt_z));
if( zr == FAILURE ) {
if( FAILURE == zend_list_close( Z_RES_P( stmt_z ))) {
LOG(SEV_ERROR, "Failed to remove stmt resource %1!d!", Z_RES_HANDLE_P(stmt_z));
}
ZVAL_NULL( stmt_z );
@ -1086,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<ss_sqlsrv_stmt*>( 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<ss_sqlsrv_stmt*>( 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& ) {

View file

@ -42,7 +42,7 @@ const int INFO_BUFFER_LEN = 256;
const char* PROCESSOR_ARCH[] = { "x86", "x64", "ia64" };
// ODBC driver name.
const char CONNECTION_STRING_DRIVER_NAME[] = "Driver={ODBC Driver 11 for SQL Server};";
const char* CONNECTION_STRING_DRIVER_NAME[] = {"Driver={ODBC Driver 13 for SQL Server};", "Driver={ODBC Driver 11 for SQL Server};"};
// default options if only the server is specified
const char CONNECTION_STRING_DEFAULT_OPTIONS[] = "Mars_Connection={Yes}";
@ -124,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<unsigned int>( 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<unsigned int>( conn_str.length() + 1 ) * sizeof( wchar_t );
wconn_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_UTF8, conn_str.c_str(), static_cast<unsigned int>( 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<SQLWCHAR*>( wconn_string.get() ),
static_cast<SQLSMALLINT>( 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<char*>( 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<unsigned int>(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<SQLWCHAR*>( wconn_string.get()),
static_cast<SQLSMALLINT>( 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<char*>( 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<DRIVER_VERSION>( 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<char*>( 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";

View file

@ -970,6 +970,14 @@ enum SERVER_VERSION {
SERVER_VERSION_2008, // use this for anything 2008 or later
};
// supported driver versions.
enum DRIVER_VERSION : size_t {
MIN = 0,
ODBC_DRIVER_13 = MIN,
ODBC_DRIVER_11 = 1,
MAX = ODBC_DRIVER_11,
};
// forward decl
struct sqlsrv_stmt;
struct stmt_option;
@ -981,6 +989,8 @@ struct sqlsrv_conn : public sqlsrv_context {
// instance variables
SERVER_VERSION server_version; // version of the server that we're connected to
DRIVER_VERSION driver_version;
// initialize with default values
sqlsrv_conn( SQLHANDLE h, error_callback e, void* drv, SQLSRV_ENCODING encoding TSRMLS_DC ) :
sqlsrv_context( h, SQL_HANDLE_DBC, e, drv, encoding )
@ -1258,7 +1268,7 @@ struct sqlsrv_stmt : public sqlsrv_context {
unsigned int current_stream_read; // # of bytes read so far. (if we read an empty PHP stream, we send an empty string
// to the server)
zval field_cache; // cache for a single row of fields, to allow multiple and out of order retrievals
zval* active_stream; // the currently active stream reading data from the database
zval active_stream; // the currently active stream reading data from the database
sqlsrv_stmt( sqlsrv_conn* c, SQLHANDLE handle, error_callback e, void* drv TSRMLS_DC );
virtual ~sqlsrv_stmt( void );
@ -1313,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<const char*>( err_msg ), CONNECTION_BUSY_ODBC_ERROR )) {
std::size_t driver_version = stmt->conn->driver_version;
if(( len == sizeof( CONNECTION_BUSY_ODBC_ERROR[driver_version] ) - 1 ) &&
!strcmp( reinterpret_cast<const char*>( err_msg ), CONNECTION_BUSY_ODBC_ERROR[driver_version] )) {
THROW_CORE_ERROR( stmt, SQLSRV_ERROR_MARS_OFF );
}

View file

@ -81,7 +81,7 @@ size_t calc_utf8_missing( sqlsrv_stmt* stmt, const char* buffer, size_t buffer_e
bool check_for_next_stream_parameter( sqlsrv_stmt* stmt TSRMLS_DC );
bool convert_input_param_to_utf16( zval* input_param_z, zval* convert_param_z );
void core_get_field_common(__inout sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_phptype
sqlsrv_php_type, __out void** field_value, __out SQLLEN* field_len TSRMLS_DC );
sqlsrv_php_type, __out void*& field_value, __out SQLLEN* field_len TSRMLS_DC);
// returns the ODBC C type constant that matches the PHP type and encoding given
SQLSMALLINT default_c_type( sqlsrv_stmt* stmt, SQLULEN paramno, zval const* param_z, SQLSRV_ENCODING encoding TSRMLS_DC );
void default_sql_size_and_scale( sqlsrv_stmt* stmt, unsigned int paramno, zval* param_z, SQLSRV_ENCODING encoding,
@ -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, &param_input_strings TSRMLS_CC );
@ -152,7 +152,7 @@ sqlsrv_stmt::sqlsrv_stmt( sqlsrv_conn* c, SQLHANDLE handle, error_callback e, vo
// desctructor for sqlsrv statement.
sqlsrv_stmt::~sqlsrv_stmt( void )
{
if( active_stream ) {
if( Z_TYPE( active_stream ) != IS_UNDEF ) {
TSRMLS_FETCH();
close_active_stream( this TSRMLS_CC );
}
@ -248,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<wchar_t> wsql_string;
@ -705,8 +699,9 @@ void core_sqlsrv_execute( sqlsrv_stmt* stmt TSRMLS_DC, const char* sql, int sql_
finalize_output_parameters( stmt TSRMLS_CC );
}
// stream parameters are sent, clean the Hashtable
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<field_cache*>(zend_hash_index_find_ptr(Z_ARRVAL(stmt->field_cache), static_cast<zend_ulong>(field_index))))) {
// the field value is NULL
if( cached->value == NULL ) {
*field_value = NULL;
*field_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<char*>( *field_value )[ cached->len ] = '\0';
}
*field_len = cached->len;
if( sqlsrv_php_type_out ) { *sqlsrv_php_type_out = static_cast<SQLSRV_PHPTYPE>( cached->type.typeinfo.type ); }
}
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<field_cache*>( zend_hash_index_find_ptr( Z_ARRVAL( stmt->field_cache ), static_cast<zend_ulong>( field_index ))))) {
// the field value is NULL
if( cached->value == NULL ) {
field_value = NULL;
*field_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<field_cache*>(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<char*>( field_value )[ cached->len ] = '\0';
}
*field_len = cached->len;
if( sqlsrv_php_type_out) { *sqlsrv_php_type_out = static_cast<SQLSRV_PHPTYPE>(cached->type.typeinfo.type); }
}
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<SQLINTEGER>( sql_field_type ), static_cast<SQLUINTEGER>( 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_PHPTYPE>( 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<field_cache*>(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<SQLINTEGER>( sql_field_type ), static_cast<SQLUINTEGER>( 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_PHPTYPE>( 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<long> field_value_temp;
field_value_temp = static_cast<long*>( 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<long> field_value_temp;
field_value_temp = static_cast<long*>( 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<double> field_value_temp;
field_value_temp = static_cast<double*>( 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<double> field_value_temp;
field_value_temp = static_cast<double*>( 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<void*>( 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<void*>( 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<sqlsrv_stream*>( stream->abstract );
ss->stmt = stmt;
ss->field_index = field_index;
ss->sql_type = static_cast<SQLUSMALLINT>( sql_type );
ss->encoding = static_cast<SQLSRV_ENCODING>( 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<void*>( 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<void*>( 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<void*>( return_value_z.get());
return_value_z.transferred();
zend_string_free( Z_STR( field_value_temp_z ));
zend_string_free( Z_STR( function_z ));
break;
}
// 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<sqlsrv_stream*>( stream->abstract );
ss->stmt = stmt;
ss->field_index = field_index;
ss->sql_type = static_cast<SQLUSMALLINT>( sql_type );
ss->encoding = static_cast<SQLSRV_ENCODING>( 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<void*>( 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<sqlsrv_output_param*>( output_param_temp );
zval* value_z = Z_REFVAL_P( output_param->param_z );
switch( Z_TYPE_P( value_z )) {
case IS_STRING:
@ -2037,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<char*>( 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<char*>( 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<char*>( 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<char*>( 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<char*>( 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<char*>( sqlsrv_realloc( field_value_temp, field_len_temp + extra + 1 ));
bool converted = convert_string_from_utf16_inplace( static_cast<SQLSRV_ENCODING>( 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<char*>( 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_ENCODING>( 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_ENCODING>( 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<char*>( 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_ENCODING>( 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;
}
}

View file

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

View file

@ -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<sqlsrv_error_const*>( Z_PTR_P(elem) );
sqlsrv_error_const* error_to_ignore = static_cast<sqlsrv_error_const*>( Z_PTR_P(elem) );
pefree(error_to_ignore, 1);
}
// called by Zend for each parameter in the g_ss_encodings_ht hash table when it is destroyed
void sqlsrv_encoding_dtor( zval* elem ) {
sqlsrv_encoding* sql_enc = reinterpret_cast<sqlsrv_encoding*>( Z_PTR_P(elem) );
sqlsrv_encoding* sql_enc = static_cast<sqlsrv_encoding*>( Z_PTR_P(elem) );
pefree(sql_enc, 1);
}
@ -611,10 +610,8 @@ PHP_RINIT_FUNCTION(sqlsrv)
#endif
SQLSRV_G( warnings_return_as_errors ) = true;
SQLSRV_G( errors ) = (zval *)sqlsrv_malloc(sizeof(zval));
ZVAL_NULL(SQLSRV_G(errors));
SQLSRV_G( warnings ) = (zval *)sqlsrv_malloc(sizeof(zval));
ZVAL_NULL(SQLSRV_G(warnings));
ZVAL_NULL( &SQLSRV_G( errors ));
ZVAL_NULL( &SQLSRV_G( warnings ));
LOG_FUNCTION( "PHP_RINIT for php_sqlsrv" );
@ -646,8 +643,8 @@ PHP_RSHUTDOWN_FUNCTION(sqlsrv)
reset_errors( TSRMLS_C );
// TODO - destruction
zval_ptr_dtor( SQLSRV_G( errors ));
zval_ptr_dtor( SQLSRV_G( warnings ));
zval_ptr_dtor( &SQLSRV_G( errors ));
zval_ptr_dtor( &SQLSRV_G( warnings ));
return SUCCESS;
}

View file

@ -10,28 +10,30 @@
#define __msodbcsql_h__
#if !defined(SQLODBC_VER)
#define SQLODBC_VER 1100
#define SQLODBC_VER 1300
#endif
#if SQLODBC_VER >= 1100
#if SQLODBC_VER >= 1300
#define SQLODBC_PRODUCT_NAME_FULL_VER_ANSI "Microsoft ODBC Driver 11 for SQL Server"
#define SQLODBC_PRODUCT_NAME_FULL_VER_ANSI "Microsoft ODBC Driver 13 for SQL Server"
#define SQLODBC_PRODUCT_NAME_FULL_ANSI "Microsoft ODBC Driver for SQL Server"
#define SQLODBC_PRODUCT_NAME_SHORT_VER_ANSI "ODBC Driver 11 for SQL Server"
#define SQLODBC_PRODUCT_NAME_SHORT_VER_ANSI "ODBC Driver 13 for SQL Server"
#define SQLODBC_PRODUCT_NAME_SHORT_ANSI "ODBC Driver for SQL Server"
#define SQLODBC_FILE_NAME_ANSI "msodbcsql"
#define SQLODBC_FILE_NAME_VER_ANSI "msodbcsql11"
#define SQLODBC_FILE_NAME_FULL_ANSI "msodbcsql11.dll"
#define SQLODBC_FILE_NAME_VER_ANSI "msodbcsql13"
#define SQLODBC_FILE_NAME_FULL_ANSI "msodbcsql13.dll"
#define SQLODBC_PRODUCT_NAME_FULL_VER_UNICODE L"Microsoft ODBC Driver 11 for SQL Server"
#define SQLODBC_PRODUCT_NAME_FULL_VER_UNICODE L"Microsoft ODBC Driver 13 for SQL Server"
#define SQLODBC_PRODUCT_NAME_FULL_UNICODE L"Microsoft ODBC Driver for SQL Server"
#define SQLODBC_PRODUCT_NAME_SHORT_VER_UNICODE L"ODBC Driver 11 for SQL Server"
#define SQLODBC_PRODUCT_NAME_SHORT_VER_UNICODE L"ODBC Driver 13 for SQL Server"
#define SQLODBC_PRODUCT_NAME_SHORT_UNICODE L"ODBC Driver for SQL Server"
#define SQLODBC_FILE_NAME_UNICODE L"msodbcsql"
#define SQLODBC_FILE_NAME_VER_UNICODE L"msodbcsql11"
#define SQLODBC_FILE_NAME_FULL_UNICODE L"msodbcsql11.dll"
#define SQLODBC_FILE_NAME_VER_UNICODE L"msodbcsql13"
#define SQLODBC_FILE_NAME_FULL_UNICODE L"msodbcsql13.dll"
// define the character type agnostic constants
#if defined(_UNICODE) || defined(UNICODE)
@ -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 <pshpack8.h> // 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 <poppack.h> // 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 <windows.h>
//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
//

View file

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

View file

@ -88,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<char*>( STDCLASS_NAME );
std::size_t class_name_len = STDCLASS_NAME_LEN;
HashTable* properties_ht = NULL;
zval retval_z;
ZVAL_UNDEF( &retval_z );
// retrieve the statement resource and optional fetch type (see enum SQLSRV_FETCH_TYPE),
// fetch style (see SQLSRV_SCROLL_* constants) and fetch offset
@ -802,8 +807,8 @@ PHP_FUNCTION( sqlsrv_fetch_object )
RETURN_NULL();
}
fetch_fields_common( stmt, SQLSRV_FETCH_ASSOC, return_value, false /*allow_empty_field_names*/ TSRMLS_CC );
properties_ht = Z_ARRVAL_P( return_value );
fetch_fields_common( stmt, SQLSRV_FETCH_ASSOC, retval_z, false /*allow_empty_field_names*/ TSRMLS_CC );
properties_ht = Z_ARRVAL( retval_z );
// find the zend_class_entry of the class the user requested (stdClass by default) for use below
zend_class_entry* class_entry = NULL;
@ -815,7 +820,7 @@ PHP_FUNCTION( sqlsrv_fetch_object )
// create an instance of the object with its default properties
// we pass NULL for the properties so that the object will be populated by its default properties
zr = object_and_properties_init( return_value, class_entry, NULL /*properties*/ );
zr = object_and_properties_init( &retval_z, class_entry, NULL /*properties*/ );
CHECK_ZEND_ERROR( zr, stmt, SS_SQLSRV_ERROR_ZEND_OBJECT_FAILED, class_name ) {
throw ss::SSException();
}
@ -825,7 +830,7 @@ PHP_FUNCTION( sqlsrv_fetch_object )
// causes duplicate properties when the visibilities are different and also references the
// default parameters directly in the object, meaning the default property value is changed when
// the object's property is changed.
zend_merge_properties( return_value, properties_ht TSRMLS_CC );
zend_merge_properties( &retval_z, properties_ht TSRMLS_CC );
// find and call the object's constructor
@ -853,16 +858,16 @@ PHP_FUNCTION( sqlsrv_fetch_object )
num_params = zend_hash_num_elements( ctor_params_ht );
params_m = reinterpret_cast<zval*>( sqlsrv_malloc( num_params * sizeof( zval ) ));
int i;
for( i = 0, zend_hash_internal_pointer_reset( ctor_params_ht );
zend_hash_has_more_elements( ctor_params_ht ) == SUCCESS;
zend_hash_move_forward( ctor_params_ht ), ++i ) {
ZVAL_COPY_VALUE( &params_m[i], zend_hash_get_current_data_ex(ctor_params_ht, &ctor_params_ht->nInternalPointer ) );
int i = 0;
zval* value_z = NULL;
ZEND_HASH_FOREACH_VAL( ctor_params_ht, value_z ) {
ZVAL_COPY_VALUE( &params_m[i], value_z );
zr = ( NULL != &params_m[i] ) ? SUCCESS : FAILURE;
CHECK_ZEND_ERROR( zr, stmt, SS_SQLSRV_ERROR_ZEND_OBJECT_FAILED, class_name ) {
throw ss::SSException();
}
} //for
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<zend_object*>(return_value);
fci.object = reinterpret_cast<zend_object*>( &retval_z );
memset( &fcic, 0, sizeof( fcic ));
fcic.initialized = 1;
fcic.function_handler = class_entry->constructor;
fcic.calling_scope = class_entry;
fci.object = reinterpret_cast<zend_object*>(return_value);
fci.object = reinterpret_cast<zend_object*>( &retval_z );
zr = zend_call_function( &fci, &fcic TSRMLS_CC );
CHECK_ZEND_ERROR( zr, stmt, SS_SQLSRV_ERROR_ZEND_OBJECT_FAILED, class_name ) {
@ -892,6 +897,7 @@ PHP_FUNCTION( sqlsrv_fetch_object )
}
} //if( class_entry->constructor )
RETURN_ZVAL( &retval_z, 1, 1 );
}
catch( core::CoreException& ) {
@ -1055,6 +1061,8 @@ PHP_FUNCTION( sqlsrv_get_field )
void* field_value = NULL;
int field_index = -1;
SQLLEN field_len = -1;
zval retval_z;
ZVAL_UNDEF(&retval_z);
// get the statement, the field index and the optional type
PROCESS_PARAMS( stmt, "rl|l", _FN_, 2, &field_index, &sqlsrv_php_type );
@ -1068,11 +1076,11 @@ PHP_FUNCTION( sqlsrv_get_field )
THROW_SS_ERROR( stmt, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ );
}
core_sqlsrv_get_field( stmt, field_index, sqlsrv_php_type, false, &field_value, &field_len, false/*cache_field*/,
core_sqlsrv_get_field( stmt, field_index, sqlsrv_php_type, false, field_value, &field_len, false/*cache_field*/,
&sqlsrv_php_type_out TSRMLS_CC );
zval* retval_z = convert_to_zval(sqlsrv_php_type_out, field_value, field_len);
RETURN_ZVAL(retval_z, 1, 1);
convert_to_zval( stmt, sqlsrv_php_type_out, field_value, field_len, retval_z );
sqlsrv_free( field_value );
RETURN_ZVAL( &retval_z, 1, 1 );
}
catch( core::CoreException& ) {
@ -1166,23 +1174,19 @@ void mark_params_by_reference( ss_sqlsrv_stmt* stmt, zval* params_z TSRMLS_DC )
HashTable* params_ht = Z_ARRVAL_P( params_z );
for( zend_hash_internal_pointer_reset( params_ht );
zend_hash_has_more_elements( params_ht ) == SUCCESS;
zend_hash_move_forward( params_ht )) {
zend_ulong index;
zend_string* key = NULL;
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<int*>( in_val )));
}
else {
ZVAL_DOUBLE( out_zval, *( static_cast<double*>( 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<int*>( in_val )));
}
else {
ZVAL_DOUBLE( &out_zval, *(static_cast<double*>( in_val )));
}
break;
}
case SQLSRV_PHPTYPE_STRING:
{
out_zval = ( zval* )sqlsrv_malloc( sizeof( zval ));
ZVAL_UNDEF( out_zval );
core::sqlsrv_zval_stringl( out_zval, reinterpret_cast<char*>( in_val ), field_len );
sqlsrv_free( in_val );
break;
}
case SQLSRV_PHPTYPE_STREAM:
case SQLSRV_PHPTYPE_DATETIME :
{
out_zval = ( static_cast<zval*>( in_val ) );
case SQLSRV_PHPTYPE_STRING:
{
ZVAL_STRINGL( &out_zval, static_cast<const char*>( 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<zval*>( in_val ));
stmt->active_stream = out_zval;
//addref here because deleting out_zval later will decrement the refcount
Z_TRY_ADDREF( out_zval );
break;
}
case SQLSRV_PHPTYPE_DATETIME:
{
out_zval = *( static_cast<zval*>( in_val ));
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<sqlsrv_fetch_field_name> field_names;
field_names = static_cast<sqlsrv_fetch_field_name*>( 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<sqlsrv_fetch_field_name> field_names;
field_names = static_cast<sqlsrv_fetch_field_name*>( 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<char*>( 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<char*>( 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<sqlsrv_encoding*>(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<sqlsrv_encoding*>( encoding_temp );
encoding_temp = NULL;
if( !stricmp( encoding_string, encoding->iana )) {
phptype_encoding.typeinfo.encoding = encoding->code_page;
return true;
}
} ZEND_HASH_FOREACH_END();
return false;
}

View file

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

View file

@ -415,7 +415,7 @@ bool ss_error_handler(sqlsrv_context& ctx, unsigned int sqlsrv_error_code, bool
severity = SEV_WARNING;
}
return handle_errors_and_warnings( ctx, SQLSRV_G( errors ), SQLSRV_G( warnings ), severity, sqlsrv_error_code, warning,
return handle_errors_and_warnings( ctx, &SQLSRV_G( errors ), &SQLSRV_G( warnings ), severity, sqlsrv_error_code, warning,
print_args TSRMLS_CC );
}
@ -464,49 +464,35 @@ PHP_FUNCTION( sqlsrv_errors )
LOG_FUNCTION( "sqlsrv_errors" );
if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flags ) == FAILURE ) {
if(( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flags ) == FAILURE ) ||
( flags != SQLSRV_ERR_ALL && flags != SQLSRV_ERR_ERRORS && flags != SQLSRV_ERR_WARNINGS )) {
LOG( SEV_ERROR, "An invalid parameter was passed to %1!s!.", _FN_ );
RETURN_FALSE;
}
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<sqlsrv_error*>(zend_hash_get_current_data_ptr( g_ss_warnings_to_ignore_ht ));
ZEND_HASH_FOREACH_KEY_PTR( g_ss_warnings_to_ignore_ht, index, key, error_temp ) {
sqlsrv_error* error = static_cast<sqlsrv_error*>( error_temp );
if (NULL == error) {
return false;
}
if( !strncmp( reinterpret_cast<char*>( error->sqlstate ), sql_state, SQL_SQLSTATE_SIZE ) &&
( error->native_code == native_code || error->native_code == -1 )) {
return true;
}
}
if( !strncmp( reinterpret_cast<char*>( 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;
}