2016-04-12 23:43:46 +02:00
# ifndef CORE_SQLSRV_H
# define CORE_SQLSRV_H
//---------------------------------------------------------------------------------------------------------------------------------
// File: core_sqlsrv.h
//
// Contents: Core routines and constants shared by the Microsoft Drivers for PHP for SQL Server
//
2016-08-24 23:28:20 +02:00
// Microsoft Drivers 4.1 for PHP for SQL Server
2016-04-12 23:43:46 +02:00
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""),
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//---------------------------------------------------------------------------------------------------------------------------------
//*********************************************************************************************************************************
// Includes
//*********************************************************************************************************************************
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
# ifdef PHP_WIN32
# define PHP_SQLSRV_API __declspec(dllexport)
# else
# define PHP_SQLSRV_API
# endif
// OACR is an internal Microsoft static code analysis tool
# if defined(OACR)
# include <oacr.h>
OACR_WARNING_PUSH
OACR_WARNING_DISABLE ( ALLOC_SIZE_OVERFLOW , " Third party code. " )
OACR_WARNING_DISABLE ( INDEX_NEGATIVE , " Third party code. " )
OACR_WARNING_DISABLE ( UNANNOTATED_BUFFER , " Third party code. " )
OACR_WARNING_DISABLE ( INDEX_UNDERFLOW , " Third party code. " )
OACR_WARNING_DISABLE ( REALLOCLEAK , " Third party code. " )
OACR_WARNING_DISABLE ( ALLOC_SIZE_OVERFLOW_WITH_ACCESS , " Third party code. " )
# else
// define to eliminate static analysis hints in the code
# define OACR_WARNING_SUPPRESS( warning, msg )
# endif
extern " C " {
# pragma warning(push)
# pragma warning( disable: 4005 4100 4127 4142 4244 4505 4530 )
# ifdef ZTS
# include "TSRM.h"
# endif
# if _MSC_VER >= 1400
// typedef and macro to prevent a conflict between php.h and ws2tcpip.h.
// php.h defines this constant as unsigned int which causes a compile error
// in ws2tcpip.h. Fortunately php.h allows an override by defining
// HAVE_SOCKLEN_T. Since ws2tcpip.h isn't included until later, we define
// socklen_t here and override the php.h version.
typedef int socklen_t ;
# define HAVE_SOCKLEN_T
# endif
# include "php.h"
# include "php_globals.h"
# include "php_ini.h"
# include "ext/standard/php_standard.h"
# include "ext/standard/info.h"
# pragma warning(pop)
# if ZEND_DEBUG
// debug build causes warning C4505 to pop up from the Zend header files
# pragma warning( disable: 4505 )
# endif
} // extern "C"
# if defined(OACR)
OACR_WARNING_POP
# endif
# include <sql.h>
# include <sqlext.h>
# if !defined(WC_ERR_INVALID_CHARS)
// imported from winnls.h as it isn't included by 5.3.0
# define WC_ERR_INVALID_CHARS 0x00000080 // error for invalid chars
# endif
// PHP defines inline as __forceinline, which in debug mode causes a warning to be emitted when
// we use std::copy, which causes compilation to fail since we compile with warnings as errors.
# if defined(ZEND_DEBUG) && defined(inline)
# undef inline
# endif
# include <deque>
# include <map>
# include <algorithm>
# include <limits>
# include <cassert>
# include <strsafe.h>
# include <memory>
// included for SQL Server specific constants
# include "msodbcsql.h"
//*********************************************************************************************************************************
// Constants and Types
//*********************************************************************************************************************************
// constants for maximums in SQL Server
const int SS_MAXCOLNAMELEN = 128 ;
const int SQL_SERVER_MAX_FIELD_SIZE = 8000 ;
const int SQL_SERVER_MAX_PRECISION = 38 ;
const int SQL_SERVER_MAX_TYPE_SIZE = 0 ;
const int SQL_SERVER_MAX_PARAMS = 2100 ;
// max size of a date time string when converting from a DateTime object to a string
const int MAX_DATETIME_STRING_LEN = 256 ;
// precision and scale for the date time types between servers
const int SQL_SERVER_2005_DEFAULT_DATETIME_PRECISION = 23 ;
const int SQL_SERVER_2005_DEFAULT_DATETIME_SCALE = 3 ;
const int SQL_SERVER_2008_DEFAULT_DATETIME_PRECISION = 34 ;
const int SQL_SERVER_2008_DEFAULT_DATETIME_SCALE = 7 ;
// types for conversions on output parameters (though they can be used for input parameters, they are ignored)
enum SQLSRV_PHPTYPE {
MIN_SQLSRV_PHPTYPE = 1 , // lowest value for a php type
SQLSRV_PHPTYPE_NULL = 1 ,
SQLSRV_PHPTYPE_INT ,
SQLSRV_PHPTYPE_FLOAT ,
SQLSRV_PHPTYPE_STRING ,
SQLSRV_PHPTYPE_DATETIME ,
SQLSRV_PHPTYPE_STREAM ,
MAX_SQLSRV_PHPTYPE , // highest value for a php type
SQLSRV_PHPTYPE_INVALID = MAX_SQLSRV_PHPTYPE // used to see if a type is invalid
} ;
// encodings supported by this extension. These basically translate into the use of SQL_C_CHAR or SQL_C_BINARY when getting
// information as a string or a stream.
enum SQLSRV_ENCODING {
SQLSRV_ENCODING_INVALID , // unknown or invalid encoding. Used to initialize variables.
SQLSRV_ENCODING_DEFAULT , // use what is the connection's default for a statement, use system if a connection
SQLSRV_ENCODING_BINARY , // use SQL_C_BINARY when using SQLGetData
SQLSRV_ENCODING_CHAR , // use SQL_C_CHAR when using SQLGetData
SQLSRV_ENCODING_SYSTEM = SQLSRV_ENCODING_CHAR ,
SQLSRV_ENCODING_UTF8 = CP_UTF8 ,
} ;
// the array keys used when returning a row via sqlsrv_fetch_array and sqlsrv_fetch_object.
enum SQLSRV_FETCH_TYPE {
MIN_SQLSRV_FETCH = 1 , // lowest value for fetch type
SQLSRV_FETCH_NUMERIC = 1 , // return an array with only numeric indices
SQLSRV_FETCH_ASSOC = 2 , // return an array with keys made from the field names
SQLSRV_FETCH_BOTH = 3 , // return an array indexed with both numbers and keys
MAX_SQLSRV_FETCH = 3 , // highest value for fetch type
} ;
// buffer size of a sql state (including the null character)
const int SQL_SQLSTATE_BUFSIZE = SQL_SQLSTATE_SIZE + 1 ;
// buffer size allocated to retrieve data from a PHP stream. This number
// was chosen since PHP doesn't return more than 8k at a time even if
// the amount requested was more.
const int PHP_STREAM_BUFFER_SIZE = 8192 ;
// SQL types for parameters encoded in an integer. The type corresponds to the SQL type ODBC constants.
// The size is the column size or precision, and scale is the decimal digits for precise numeric types.
union sqlsrv_sqltype {
struct typeinfo_t {
int type : 9 ;
int size : 14 ;
int scale : 8 ;
} typeinfo ;
zend_long value ;
} ;
// SQLSRV PHP types (as opposed to the Zend PHP type constants). Contains the type (see SQLSRV_PHPTYPE)
// and the encoding for strings and streams (see SQLSRV_ENCODING)
union sqlsrv_phptype {
struct typeinfo_t {
unsigned type : 8 ;
unsigned encoding : 16 ;
} typeinfo ;
zend_long value ;
} ;
// static assert for enforcing compile time conditions
template < bool b >
struct sqlsrv_static_assert ;
template < >
struct sqlsrv_static_assert < true > { static const int value = 1 ; } ;
# define SQLSRV_STATIC_ASSERT( c ) (sqlsrv_static_assert<(c) != 0>() )
//*********************************************************************************************************************************
// Logging
//*********************************************************************************************************************************
// log_callback
// a driver specific callback for logging messages
// severity - severity of the message: notice, warning, or error
// msg - the message to log in a FormatMessage style formatting
// print_args - args to the message
typedef void ( * log_callback ) ( unsigned int severity TSRMLS_DC , const char * msg , va_list * print_args ) ;
// each driver must register a log callback. This should be the first thing a driver does.
void core_sqlsrv_register_logger ( log_callback ) ;
// a simple wrapper around a PHP error logging function.
void write_to_log ( unsigned int severity TSRMLS_DC , const char * msg , . . . ) ;
// a macro to make it convenient to use the function.
# define LOG( severity, msg, ...) write_to_log( severity TSRMLS_CC, msg, __VA_ARGS__ )
// mask for filtering which severities are written to the log
enum logging_severity {
SEV_ERROR = 0x01 ,
SEV_WARNING = 0x02 ,
SEV_NOTICE = 0x04 ,
SEV_ALL = - 1 ,
} ;
// Kill the PHP process and log the message to PHP
void die ( const char * msg , . . . ) ;
# define DIE( msg, ... ) { die( msg, __VA_ARGS__ ); }
//*********************************************************************************************************************************
// Resource/Memory Management
//*********************************************************************************************************************************
// the macro max is defined and overrides the call to max in the allocator class
# pragma push_macro( "max" )
# undef max
// new memory allocation/free debugging facilities to help us verify that all allocations are being
// released in a timely manner and not just at the end of the script.
// Zend has memory logging and checking, but it can generate a lot of noise for just one extension.
// It's meant for internal use but might be useful for people adding features to our extension.
// To use it, uncomment the #define below and compile in Debug NTS. All allocations and releases
// must be done with sqlsrv_malloc and sqlsrv_free.
// #define SQLSRV_MEM_DEBUG 1
# if defined( PHP_DEBUG ) && !defined( ZTS ) && defined( SQLSRV_MEM_DEBUG )
inline void * sqlsrv_malloc_trace ( size_t size , const char * file , int line )
{
void * ptr = emalloc ( size ) ;
LOG ( SEV_NOTICE , " emalloc returned %4!08x!: %1!d! bytes at %2!s!:%3!d! " , size , file , line , ptr ) ;
return ptr ;
}
inline void * sqlsrv_malloc_trace ( size_t element_count , size_t element_size , size_t extra , const char * file , int line )
{
OACR_WARNING_SUPPRESS ( ALLOC_SIZE_OVERFLOW_IN_ALLOC_WRAPPER , " Overflow verified below " ) ;
if ( ( element_count > 0 & & element_size > 0 ) & &
( element_count > element_size * element_count | | element_size > element_size * element_count ) ) {
DIE ( " Integer overflow in sqlsrv_malloc " ) ;
}
if ( element_size * element_count > element_size * element_count + extra ) {
DIE ( " Integer overflow in sqlsrv_malloc " ) ;
}
if ( element_size * element_count + extra = = 0 ) {
DIE ( " Allocation size must be more than 0 " ) ;
}
void * ptr = emalloc ( element_size * element_count + extra ) ;
LOG ( SEV_NOTICE , " emalloc returned %4!08x!: %1!d! bytes at %2!s!:%3!d! " , size , file , line , ptr ) ;
return ptr ;
}
inline void * sqlsrv_realloc_trace ( void * buffer , size_t size , const char * file , int line )
{
void * ptr = erealloc ( original , size ) ;
LOG ( SEV_NOTICE , " erealloc returned %5!08x! from %4!08x!: %1!d! bytes at %2!s!:%3!d! " , size , file , line , ptr , original ) ;
return ptr ;
}
inline void sqlsrv_free_trace ( void * ptr , const char * file , int line )
{
LOG ( SEV_NOTICE , " efree %1!08x! at %2!s!:%3!d! " , ptr , file , line ) ;
efree ( ptr ) ;
}
# define sqlsrv_malloc( size ) sqlsrv_malloc_trace( size, __FILE__, __LINE__ )
# define sqlsrv_malloc( count, size, extra ) sqlsrv_malloc_trace( count, size, extra, __FILE__, __LINE__ )
# define sqlsrv_realloc( buffer, size ) sqlsrv_realloc_trace( buffer, size, __FILE__, __LINE__ )
# define sqlsrv_free( ptr ) sqlsrv_free_trace( ptr, __FILE__, __LINE__ )
# else
inline void * sqlsrv_malloc ( size_t size )
{
return emalloc ( size ) ;
}
inline void * sqlsrv_malloc ( size_t element_count , size_t element_size , size_t extra )
{
OACR_WARNING_SUPPRESS ( ALLOC_SIZE_OVERFLOW_IN_ALLOC_WRAPPER , " Overflow verified below " ) ;
if ( ( element_count > 0 & & element_size > 0 ) & &
( element_count > element_size * element_count | | element_size > element_size * element_count ) ) {
DIE ( " Integer overflow in sqlsrv_malloc " ) ;
}
if ( element_size * element_count > element_size * element_count + extra ) {
DIE ( " Integer overflow in sqlsrv_malloc " ) ;
}
if ( element_size * element_count + extra = = 0 ) {
DIE ( " Allocation size must be more than 0 " ) ;
}
return emalloc ( element_size * element_count + extra ) ;
}
inline void * sqlsrv_realloc ( void * buffer , size_t size )
{
return erealloc ( buffer , size ) ;
}
inline void sqlsrv_free ( void * ptr )
{
efree ( ptr ) ;
}
# endif
// trait class that allows us to assign const types to an auto_ptr
template < typename T >
struct remove_const {
typedef T type ;
} ;
template < typename T >
struct remove_const < const T * > {
typedef T * type ;
} ;
// allocator that uses the zend memory manager to manage memory
// this allows us to use STL classes that still work with Zend objects
template < typename T >
struct sqlsrv_allocator {
// typedefs used by the STL classes
typedef T value_type ;
typedef value_type * pointer ;
typedef const value_type * const_pointer ;
typedef value_type & reference ;
typedef const value_type & const_reference ;
typedef std : : size_t size_type ;
typedef std : : ptrdiff_t difference_type ;
// conversion typedef (used by list and other STL classes)
template < typename U >
struct rebind {
typedef sqlsrv_allocator < U > other ;
} ;
inline sqlsrv_allocator ( ) { }
inline ~ sqlsrv_allocator ( ) { }
inline sqlsrv_allocator ( sqlsrv_allocator const & ) { }
template < typename U >
inline sqlsrv_allocator ( sqlsrv_allocator < U > const & ) { }
// address (doesn't work if the class defines operator&)
inline pointer address ( reference r )
{
return & r ;
}
inline const_pointer address ( const_reference r )
{
return & r ;
}
// memory allocation/deallocation
inline pointer allocate ( size_type cnt ,
typename std : : allocator < void > : : const_pointer = 0 )
{
return reinterpret_cast < pointer > ( sqlsrv_malloc ( cnt , sizeof ( T ) , 0 ) ) ;
}
inline void deallocate ( pointer p , size_type )
{
sqlsrv_free ( p ) ;
}
// size
inline size_type max_size ( void ) const
{
return std : : numeric_limits < size_type > : : max ( ) / sizeof ( T ) ;
}
// object construction/destruction
inline void construct ( pointer p , const T & t )
{
new ( p ) T ( t ) ;
}
inline void destroy ( pointer p )
{
p - > ~ T ( ) ;
}
// equality operators
inline bool operator = = ( sqlsrv_allocator const & )
{
return true ;
}
inline bool operator ! = ( sqlsrv_allocator const & a )
{
return ! operator = = ( a ) ;
}
} ;
// base class for auto_ptrs that we define below. It provides common operators and functions
// used by all the classes.
template < typename T , typename Subclass >
class sqlsrv_auto_ptr {
public :
sqlsrv_auto_ptr ( void ) : _ptr ( NULL )
{
}
~ sqlsrv_auto_ptr ( void )
{
static_cast < Subclass * > ( this ) - > reset ( NULL ) ;
}
// call when ownership is transferred
void transferred ( void )
{
_ptr = NULL ;
}
// explicit function to get the pointer.
T * get ( void ) const
{
return _ptr ;
}
// cast operator to allow auto_ptr to be used where a normal const * can be.
operator const T * ( ) const
{
return _ptr ;
}
// cast operator to allow auto_ptr to be used where a normal pointer can be.
operator typename remove_const < T * > : : type ( ) const
{
return _ptr ;
}
operator bool ( ) const
{
return _ptr ! = NULL ;
}
// there are a number of places where we allocate a block intended to be accessed as
// an array of elements, so this operator allows us to treat the memory as such.
T & operator [ ] ( int index ) const
{
return _ptr [ index ] ;
}
// there are a number of places where we allocate a block intended to be accessed as
// an array of elements, so this operator allows us to treat the memory as such.
T & operator [ ] ( unsigned int index ) const
{
return _ptr [ index ] ;
}
// there are a number of places where we allocate a block intended to be accessed as
// an array of elements, so this operator allows us to treat the memory as such.
T & operator [ ] ( long index ) const
{
return _ptr [ index ] ;
}
# ifdef __WIN64
// there are a number of places where we allocate a block intended to be accessed as
// an array of elements, so this operator allows us to treat the memory as such.
T & operator [ ] ( std : : size_t index ) const
{
return _ptr [ index ] ;
}
# endif
// there are a number of places where we allocate a block intended to be accessed as
// an array of elements, so this operator allows us to treat the memory as such.
T & operator [ ] ( unsigned short index ) const
{
return _ptr [ index ] ;
}
// access elements of a structure through the auto ptr
T * const operator - > ( void ) const
{
return _ptr ;
}
// value from reference operator (i.e., i = *(&i); or *i = blah;)
T & operator * ( ) const
{
return * _ptr ;
}
// allow the use of the address-of operator to simulate a **.
// Note: this operator conflicts with storing these within an STL container. If you need
// to do that, then redefine this as getpp and change instances of &auto_ptr to auto_ptr.getpp()
T * * operator & ( void )
{
return & _ptr ;
}
protected :
sqlsrv_auto_ptr ( T * ptr ) :
_ptr ( ptr )
{
}
sqlsrv_auto_ptr ( sqlsrv_auto_ptr & src )
{
if ( _ptr ) {
static_cast < Subclass * > ( this ) - > reset ( src . _ptr ) ;
}
src . transferred ( ) ;
}
// assign a new pointer to the auto_ptr. It will free the previous memory block
// because ownership is deemed finished.
T * operator = ( T * ptr )
{
static_cast < Subclass * > ( this ) - > reset ( ptr ) ;
return ptr ;
}
T * _ptr ;
} ;
// an auto_ptr for sqlsrv_malloc/sqlsrv_free. When allocating a chunk of memory using sqlsrv_malloc, wrap that pointer
// in a variable of sqlsrv_malloc_auto_ptr. sqlsrv_malloc_auto_ptr will "own" that block and assure that it is
// freed until the variable is destroyed (out of scope) or ownership is transferred using the function
// "transferred".
// DO NOT CALL sqlsrv_realloc with a sqlsrv_malloc_auto_ptr. Use the resize member function.
template < typename T >
class sqlsrv_malloc_auto_ptr : public sqlsrv_auto_ptr < T , sqlsrv_malloc_auto_ptr < T > > {
public :
sqlsrv_malloc_auto_ptr ( void ) :
sqlsrv_auto_ptr < T , sqlsrv_malloc_auto_ptr < T > > ( NULL )
{
}
sqlsrv_malloc_auto_ptr ( const sqlsrv_malloc_auto_ptr & src ) :
sqlsrv_auto_ptr < T , sqlsrv_malloc_auto_ptr < T > > ( src )
{
}
// free the original pointer and assign a new pointer. Use NULL to simply free the pointer.
void reset ( T * ptr = NULL )
{
if ( _ptr )
sqlsrv_free ( ( void * ) _ptr ) ;
_ptr = ptr ;
}
T * operator = ( T * ptr )
{
return sqlsrv_auto_ptr < T , sqlsrv_malloc_auto_ptr < T > > : : operator = ( ptr ) ;
}
void operator = ( sqlsrv_malloc_auto_ptr < T > & src )
{
T * p = src . get ( ) ;
src . transferred ( ) ;
this - > _ptr = p ;
}
// DO NOT CALL sqlsrv_realloc with a sqlsrv_malloc_auto_ptr. Use the resize member function.
// has the same parameter list as sqlsrv_realloc: new_size is the size in bytes of the newly allocated buffer
void resize ( size_t new_size )
{
_ptr = reinterpret_cast < T * > ( sqlsrv_realloc ( _ptr , new_size ) ) ;
}
} ;
// auto ptr for Zend hash tables. Used to clean up a hash table allocated when
// something caused an early exit from the function. This is used when the hash_table is
// allocated in a zval that itself can't be released. Otherwise, use the zval_auto_ptr.
class hash_auto_ptr : public sqlsrv_auto_ptr < HashTable , hash_auto_ptr > {
public :
hash_auto_ptr ( void ) :
sqlsrv_auto_ptr < HashTable , hash_auto_ptr > ( NULL )
{
}
// free the original pointer and assign a new pointer. Use NULL to simply free the pointer.
void reset ( HashTable * ptr = NULL )
{
if ( _ptr ) {
zend_hash_destroy ( _ptr ) ;
FREE_HASHTABLE ( _ptr ) ;
}
_ptr = ptr ;
}
HashTable * operator = ( HashTable * ptr )
{
return sqlsrv_auto_ptr < HashTable , hash_auto_ptr > : : operator = ( ptr ) ;
}
private :
hash_auto_ptr ( HashTable const & hash ) ;
hash_auto_ptr ( hash_auto_ptr const & hash ) ;
} ;
// an auto_ptr for zvals. When allocating a zval, wrap that pointer in a variable of zval_auto_ptr.
// zval_auto_ptr will "own" that zval and assure that it is freed when the variable is destroyed
// (out of scope) or ownership is transferred using the function "transferred".
class zval_auto_ptr : public sqlsrv_auto_ptr < zval , zval_auto_ptr > {
public :
zval_auto_ptr ( void )
{
}
// free the original pointer and assign a new pointer. Use NULL to simply free the pointer.
void reset ( zval * ptr = NULL )
{
if ( _ptr )
zval_ptr_dtor ( _ptr ) ;
_ptr = ptr ;
}
zval * operator = ( zval * ptr )
{
return sqlsrv_auto_ptr < zval , zval_auto_ptr > : : operator = ( ptr ) ;
}
private :
zval_auto_ptr ( const zval_auto_ptr & src ) ;
} ;
# pragma pop_macro( "max" )
//*********************************************************************************************************************************
// sqlsrv_error
//*********************************************************************************************************************************
// *** PHP specific errors ***
// sqlsrv errors are held in a structure of this type used by the driver handle_error functions
// format is a flag that tells the driver error handler functions if there are parameters to use with FormatMessage
// into the error message before returning it.
// base class which can be instatiated with aggregates (see error constants)
struct sqlsrv_error_const {
SQLCHAR * sqlstate ;
SQLCHAR * native_message ;
SQLINTEGER native_code ;
bool format ;
} ;
// subclass which is used by the core layer to instantiate ODBC errors
struct sqlsrv_error : public sqlsrv_error_const {
sqlsrv_error ( void )
{
sqlstate = NULL ;
native_message = NULL ;
native_code = - 1 ;
format = false ;
}
sqlsrv_error ( SQLCHAR * sql_state , SQLCHAR * message , SQLINTEGER code , bool printf_format = false )
{
sqlstate = reinterpret_cast < SQLCHAR * > ( sqlsrv_malloc ( SQL_SQLSTATE_BUFSIZE ) ) ;
native_message = reinterpret_cast < SQLCHAR * > ( sqlsrv_malloc ( SQL_MAX_MESSAGE_LENGTH + 1 ) ) ;
strcpy_s ( reinterpret_cast < char * > ( sqlstate ) , SQL_SQLSTATE_BUFSIZE , reinterpret_cast < const char * > ( sql_state ) ) ;
strcpy_s ( reinterpret_cast < char * > ( native_message ) , SQL_MAX_MESSAGE_LENGTH + 1 , reinterpret_cast < const char * > ( message ) ) ;
native_code = code ;
format = printf_format ;
}
sqlsrv_error ( sqlsrv_error_const const & prototype )
{
sqlsrv_error ( prototype . sqlstate , prototype . native_message , prototype . native_code , prototype . format ) ;
}
~ sqlsrv_error ( void )
{
if ( sqlstate ! = NULL ) {
sqlsrv_free ( sqlstate ) ;
}
if ( native_message ! = NULL ) {
sqlsrv_free ( native_message ) ;
}
}
} ;
// an auto_ptr for sqlsrv_errors. These call the destructor explicitly rather than call delete
class sqlsrv_error_auto_ptr : public sqlsrv_auto_ptr < sqlsrv_error , sqlsrv_error_auto_ptr > {
public :
sqlsrv_error_auto_ptr ( void ) :
sqlsrv_auto_ptr < sqlsrv_error , sqlsrv_error_auto_ptr > ( NULL )
{
}
sqlsrv_error_auto_ptr ( sqlsrv_error_auto_ptr const & src ) :
sqlsrv_auto_ptr < sqlsrv_error , sqlsrv_error_auto_ptr > ( ( sqlsrv_error_auto_ptr & ) src )
{
}
// free the original pointer and assign a new pointer. Use NULL to simply free the pointer.
void reset ( sqlsrv_error * ptr = NULL )
{
if ( _ptr ) {
_ptr - > ~ sqlsrv_error ( ) ;
sqlsrv_free ( ( void * ) _ptr ) ;
}
_ptr = ptr ;
}
sqlsrv_error * operator = ( sqlsrv_error * ptr )
{
return sqlsrv_auto_ptr < sqlsrv_error , sqlsrv_error_auto_ptr > : : operator = ( ptr ) ;
}
// unlike traditional assignment operators, the chained assignment of an auto_ptr doesn't make much
// sense. Only the last one would have anything in it.
void operator = ( sqlsrv_error_auto_ptr & src )
{
sqlsrv_error * p = src . get ( ) ;
src . transferred ( ) ;
this - > _ptr = p ;
}
} ;
//*********************************************************************************************************************************
// Context
//*********************************************************************************************************************************
class sqlsrv_context ;
struct sqlsrv_conn ;
// error_callback
// a driver specific callback for processing errors.
// ctx - the context holding the handles
// sqlsrv_error_code - specific error code to return.
typedef bool ( * error_callback ) ( sqlsrv_context & ctx , unsigned int sqlsrv_error_code , bool error TSRMLS_DC , va_list * print_args ) ;
// sqlsrv_context
// a context holds relevant information to be passed with a connection and statement objects.
class sqlsrv_context {
public :
sqlsrv_context ( SQLSMALLINT type , error_callback e , void * drv , SQLSRV_ENCODING encoding = SQLSRV_ENCODING_INVALID ) :
handle_ ( SQL_NULL_HANDLE ) ,
handle_type_ ( type ) ,
err_ ( e ) ,
name_ ( NULL ) ,
driver_ ( drv ) ,
last_error_ ( ) ,
encoding_ ( encoding )
{
}
sqlsrv_context ( SQLHANDLE h , SQLSMALLINT t , error_callback e , void * drv , SQLSRV_ENCODING encoding = SQLSRV_ENCODING_INVALID ) :
handle_ ( h ) ,
handle_type_ ( t ) ,
err_ ( e ) ,
name_ ( NULL ) ,
driver_ ( drv ) ,
last_error_ ( ) ,
encoding_ ( encoding )
{
}
sqlsrv_context ( sqlsrv_context const & ctx ) :
handle_ ( ctx . handle_ ) ,
handle_type_ ( ctx . handle_type_ ) ,
err_ ( ctx . err_ ) ,
name_ ( ctx . name_ ) ,
driver_ ( ctx . driver_ ) ,
last_error_ ( ctx . last_error_ )
{
}
void set_func ( const char * f )
{
name_ = f ;
}
void set_last_error ( sqlsrv_error_auto_ptr & last_error )
{
last_error_ = last_error ;
}
sqlsrv_error_auto_ptr & last_error ( void )
{
return last_error_ ;
}
// since the primary responsibility of a context is to hold an ODBC handle, we
// provide these convenience operators for using them interchangeably
operator SQLHANDLE ( void ) const
{
return handle_ ;
}
error_callback error_handler ( void ) const
{
return err_ ;
}
SQLHANDLE handle ( void ) const
{
return handle_ ;
}
SQLSMALLINT handle_type ( void ) const
{
return handle_type_ ;
}
const char * func ( void ) const
{
return name_ ;
}
void * driver ( void ) const
{
return driver_ ;
}
void set_driver ( void * driver )
{
this - > driver_ = driver ;
}
void invalidate ( void )
{
if ( handle_ ! = SQL_NULL_HANDLE ) {
: : SQLFreeHandle ( handle_type_ , handle_ ) ;
2016-07-06 20:47:05 +02:00
last_error_ . reset ( ) ;
2016-04-12 23:43:46 +02:00
}
handle_ = SQL_NULL_HANDLE ;
}
bool valid ( void )
{
return handle_ ! = SQL_NULL_HANDLE ;
}
SQLSRV_ENCODING encoding ( void ) const
{
return encoding_ ;
}
void set_encoding ( SQLSRV_ENCODING e )
{
encoding_ = e ;
}
private :
SQLHANDLE handle_ ; // ODBC handle for this context
SQLSMALLINT handle_type_ ; // type of the ODBC handle
const char * name_ ; // function name currently executing this context
error_callback err_ ; // driver error callback if error occurs in core layer
void * driver_ ; // points back to the driver for PDO
sqlsrv_error_auto_ptr last_error_ ; // last error that happened on this object
SQLSRV_ENCODING encoding_ ; // encoding of the context
} ;
const int SQLSRV_OS_VISTA_OR_LATER = 6 ; // major version for Vista
// maps an IANA encoding to a code page
struct sqlsrv_encoding {
const char * iana ;
size_t iana_len ;
unsigned int code_page ;
bool not_for_connection ;
sqlsrv_encoding ( const char * iana , unsigned int code_page , bool not_for_conn = false ) :
iana ( iana ) , iana_len ( strlen ( iana ) ) , code_page ( code_page ) , not_for_connection ( not_for_conn )
{
}
} ;
//*********************************************************************************************************************************
// Initialization
//*********************************************************************************************************************************
// variables set during initialization
extern OSVERSIONINFO g_osversion ; // used to determine which OS we're running in
extern HashTable * g_encodings ; // encodings supported by this driver
void core_sqlsrv_minit ( sqlsrv_context * * henv_cp , sqlsrv_context * * henv_ncp , error_callback err , const char * driver_func TSRMLS_DC ) ;
void core_sqlsrv_mshutdown ( sqlsrv_context & henv_cp , sqlsrv_context & henv_ncp ) ;
// environment context used by sqlsrv_connect for when a connection error occurs.
struct sqlsrv_henv {
sqlsrv_context ctx ;
sqlsrv_henv ( SQLHANDLE handle , error_callback e , void * drv ) :
ctx ( handle , SQL_HANDLE_ENV , e , drv )
{
}
} ;
//*********************************************************************************************************************************
// Connection
//*********************************************************************************************************************************
// supported server versions (determined at connection time)
enum SERVER_VERSION {
SERVER_VERSION_UNKNOWN = - 1 ,
SERVER_VERSION_2000 = 8 ,
SERVER_VERSION_2005 ,
SERVER_VERSION_2008 , // use this for anything 2008 or later
} ;
2016-06-13 23:44:53 +02:00
// supported driver versions.
enum DRIVER_VERSION : size_t {
MIN = 0 ,
ODBC_DRIVER_13 = MIN ,
ODBC_DRIVER_11 = 1 ,
MAX = ODBC_DRIVER_11 ,
} ;
2016-04-12 23:43:46 +02:00
// forward decl
struct sqlsrv_stmt ;
struct stmt_option ;
// *** connection resource structure ***
// this is the resource structure returned when a connection is made.
struct sqlsrv_conn : public sqlsrv_context {
// instance variables
SERVER_VERSION server_version ; // version of the server that we're connected to
2016-06-13 23:44:53 +02:00
DRIVER_VERSION driver_version ;
2016-04-12 23:43:46 +02:00
// 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 )
{
}
// sqlsrv_conn has no destructor since its allocated using placement new, which requires that the destructor be
// called manually. Instead, we leave it to the allocator to invalidate the handle when an error occurs allocating
// the sqlsrv_conn with a connection.
} ;
enum SQLSRV_STMT_OPTIONS {
SQLSRV_STMT_OPTION_INVALID ,
SQLSRV_STMT_OPTION_QUERY_TIMEOUT ,
SQLSRV_STMT_OPTION_SEND_STREAMS_AT_EXEC ,
SQLSRV_STMT_OPTION_SCROLLABLE ,
SQLSRV_STMT_OPTION_CLIENT_BUFFER_MAX_SIZE ,
// Driver specific connection options
SQLSRV_STMT_OPTION_DRIVER_SPECIFIC = 1000 ,
} ;
namespace ODBCConnOptions {
const char APP [ ] = " APP " ;
const char ApplicationIntent [ ] = " ApplicationIntent " ;
const char AttachDBFileName [ ] = " AttachDbFileName " ;
const char CharacterSet [ ] = " CharacterSet " ;
const char ConnectionPooling [ ] = " ConnectionPooling " ;
const char Database [ ] = " Database " ;
const char Encrypt [ ] = " Encrypt " ;
const char Failover_Partner [ ] = " Failover_Partner " ;
const char LoginTimeout [ ] = " LoginTimeout " ;
const char MARS_ODBC [ ] = " MARS_Connection " ;
const char MultiSubnetFailover [ ] = " MultiSubnetFailover " ;
const char QuotedId [ ] = " QuotedId " ;
const char TraceFile [ ] = " TraceFile " ;
const char TraceOn [ ] = " TraceOn " ;
const char TrustServerCertificate [ ] = " TrustServerCertificate " ;
const char TransactionIsolation [ ] = " TransactionIsolation " ;
const char WSID [ ] = " WSID " ;
const char UID [ ] = " UID " ;
const char PWD [ ] = " PWD " ;
const char SERVER [ ] = " Server " ;
}
enum SQLSRV_CONN_OPTIONS {
SQLSRV_CONN_OPTION_INVALID ,
SQLSRV_CONN_OPTION_APP ,
SQLSRV_CONN_OPTION_CHARACTERSET ,
SQLSRV_CONN_OPTION_CONN_POOLING ,
SQLSRV_CONN_OPTION_DATABASE ,
SQLSRV_CONN_OPTION_ENCRYPT ,
SQLSRV_CONN_OPTION_FAILOVER_PARTNER ,
SQLSRV_CONN_OPTION_LOGIN_TIMEOUT ,
SQLSRV_CONN_OPTION_MARS ,
SQLSRV_CONN_OPTION_QUOTED_ID ,
SQLSRV_CONN_OPTION_TRACE_FILE ,
SQLSRV_CONN_OPTION_TRACE_ON ,
SQLSRV_CONN_OPTION_TRANS_ISOLATION ,
SQLSRV_CONN_OPTION_TRUST_SERVER_CERT ,
SQLSRV_CONN_OPTION_WSID ,
SQLSRV_CONN_OPTION_ATTACHDBFILENAME ,
SQLSRV_CONN_OPTION_APPLICATION_INTENT ,
SQLSRV_CONN_OPTION_MULTI_SUBNET_FAILOVER ,
// Driver specific connection options
SQLSRV_CONN_OPTION_DRIVER_SPECIFIC = 1000 ,
} ;
# define NO_ATTRIBUTE -1
// type of connection attributes
enum CONN_ATTR_TYPE {
CONN_ATTR_INT ,
CONN_ATTR_BOOL ,
CONN_ATTR_STRING ,
CONN_ATTR_INVALID ,
} ;
// a connection option that includes the callback function that handles that option (e.g., adds it to the connection string or
// sets an attribute)
struct connection_option {
// the name of the option as passed in by the user
const char * sqlsrv_name ;
unsigned int sqlsrv_len ;
unsigned int conn_option_key ;
// the name of the option in the ODBC connection string
const char * odbc_name ;
unsigned int odbc_len ;
enum CONN_ATTR_TYPE value_type ;
// process the connection type
// return whether or not the function was successful in processing the connection option
void ( * func ) ( connection_option const * , zval * value , sqlsrv_conn * conn , std : : string & conn_str TSRMLS_DC ) ;
} ;
// connection attribute functions
template < unsigned int Attr >
struct str_conn_attr_func {
static void func ( connection_option const * /*option*/ , zval * value , sqlsrv_conn * conn , std : : string & /*conn_str*/ TSRMLS_DC )
{
try {
core : : SQLSetConnectAttr ( conn , Attr , reinterpret_cast < SQLPOINTER > ( Z_STRVAL_P ( value ) ) ,
static_cast < SQLINTEGER > ( Z_STRLEN_P ( value ) ) TSRMLS_CC ) ;
}
catch ( core : : CoreException & ) {
throw ;
}
}
} ;
// simply add the parsed value to the connection string
struct conn_str_append_func {
static void func ( connection_option const * option , zval * value , sqlsrv_conn * /*conn*/ , std : : string & conn_str TSRMLS_DC ) ;
} ;
struct conn_null_func {
static void func ( connection_option const * /*option*/ , zval * /*value*/ , sqlsrv_conn * /*conn*/ , std : : string & /*conn_str*/
TSRMLS_DC ) ;
} ;
// factory to create a connection (since they are subclassed to instantiate statements)
typedef sqlsrv_conn * ( * driver_conn_factory ) ( SQLHANDLE h , error_callback e , void * drv TSRMLS_DC ) ;
// *** connection functions ***
sqlsrv_conn * core_sqlsrv_connect ( sqlsrv_context & henv_cp , sqlsrv_context & henv_ncp , driver_conn_factory conn_factory ,
const char * server , const char * uid , const char * pwd ,
HashTable * options_ht , error_callback err , const connection_option driver_conn_opt_list [ ] ,
void * driver , const char * driver_func TSRMLS_DC ) ;
void core_sqlsrv_close ( sqlsrv_conn * conn TSRMLS_DC ) ;
void core_sqlsrv_prepare ( sqlsrv_stmt * stmt , const char * sql , SQLLEN sql_len TSRMLS_DC ) ;
void core_sqlsrv_begin_transaction ( sqlsrv_conn * conn TSRMLS_DC ) ;
void core_sqlsrv_commit ( sqlsrv_conn * conn TSRMLS_DC ) ;
void core_sqlsrv_rollback ( sqlsrv_conn * conn TSRMLS_DC ) ;
2016-07-06 20:47:05 +02:00
void core_sqlsrv_get_server_info ( sqlsrv_conn * conn , _Out_ zval * server_info TSRMLS_DC ) ;
void core_sqlsrv_get_server_version ( sqlsrv_conn * conn , _Out_ zval * server_version TSRMLS_DC ) ;
void core_sqlsrv_get_client_info ( sqlsrv_conn * conn , _Out_ zval * client_info TSRMLS_DC ) ;
2016-04-12 23:43:46 +02:00
bool core_is_conn_opt_value_escaped ( const char * value , size_t value_len ) ;
size_t core_str_zval_is_true ( zval * str_zval ) ;
//*********************************************************************************************************************************
// Statement
//*********************************************************************************************************************************
struct stmt_option_functor {
virtual void operator ( ) ( sqlsrv_stmt * /*stmt*/ , stmt_option const * /*opt*/ , zval * /*value_z*/ TSRMLS_DC ) ;
} ;
struct stmt_option_query_timeout : public stmt_option_functor {
virtual void operator ( ) ( sqlsrv_stmt * stmt , stmt_option const * opt , zval * value_z TSRMLS_DC ) ;
} ;
struct stmt_option_send_at_exec : public stmt_option_functor {
virtual void operator ( ) ( sqlsrv_stmt * stmt , stmt_option const * opt , zval * value_z TSRMLS_DC ) ;
} ;
struct stmt_option_buffered_query_limit : public stmt_option_functor {
virtual void operator ( ) ( sqlsrv_stmt * stmt , stmt_option const * opt , zval * value_z TSRMLS_DC ) ;
} ;
// used to hold the table for statment options
struct stmt_option {
const char * name ; // name of the statement option
unsigned int name_len ; // name length
unsigned int key ;
std : : unique_ptr < stmt_option_functor > func ; // callback that actually handles the work of the option
} ;
// holds the stream param and the encoding that it was assigned
struct sqlsrv_stream {
zval * stream_z ;
SQLSRV_ENCODING encoding ;
SQLUSMALLINT field_index ;
SQLSMALLINT sql_type ;
sqlsrv_stmt * stmt ;
std : : size_t stmt_index ;
sqlsrv_stream ( zval * str_z , SQLSRV_ENCODING enc ) :
stream_z ( str_z ) , encoding ( enc )
{
}
sqlsrv_stream ( ) : stream_z ( NULL ) , encoding ( SQLSRV_ENCODING_INVALID ) , stmt ( NULL )
{
}
} ;
// close any active stream
2016-07-06 20:47:05 +02:00
void close_active_stream ( _Inout_ sqlsrv_stmt * stmt TSRMLS_DC ) ;
2016-04-12 23:43:46 +02:00
extern php_stream_wrapper g_sqlsrv_stream_wrapper ;
// resource constants used when registering the stream type with PHP
# define SQLSRV_STREAM_WRAPPER "sqlsrv"
# define SQLSRV_STREAM "sqlsrv_stream"
// holds the output parameter information. Strings also need the encoding and other information for
// after processing. Only integer, float, and strings are allowable output parameters.
struct sqlsrv_output_param {
zval * param_z ;
SQLSRV_ENCODING encoding ;
SQLUSMALLINT param_num ; // used to index into the ind_or_len of the statement
SQLLEN original_buffer_len ; // used to make sure the returned length didn't overflow the buffer
bool is_bool ;
// string output param constructor
sqlsrv_output_param ( zval * p_z , SQLSRV_ENCODING enc , int num , SQLUINTEGER buffer_len ) :
param_z ( p_z ) , encoding ( enc ) , param_num ( num ) , original_buffer_len ( buffer_len ) , is_bool ( false )
{
}
// every other type output parameter constructor
sqlsrv_output_param ( zval * p_z , int num , bool is_bool ) :
param_z ( p_z ) ,
param_num ( num ) ,
encoding ( SQLSRV_ENCODING_INVALID ) ,
original_buffer_len ( - 1 ) ,
is_bool ( is_bool )
{
}
} ;
// forward decls
struct sqlsrv_result_set ;
// *** Statement resource structure ***
struct sqlsrv_stmt : public sqlsrv_context {
void free_param_data ( TSRMLS_D ) ;
virtual void new_result_set ( TSRMLS_D ) ;
sqlsrv_conn * conn ; // Connection that created this statement
bool executed ; // Whether the statement has been executed yet (used for error messages)
bool past_fetch_end ; // Core_sqlsrv_fetch sets this field when the statement goes beyond the last row
sqlsrv_result_set * current_results ; // Current result set
SQLULEN cursor_type ; // Type of cursor for the current result set
bool has_rows ; // Has_rows is set if there are actual rows in the row set
bool fetch_called ; // Used by core_sqlsrv_get_field to return an informative error if fetch not yet called
int last_field_index ; // last field retrieved by core_sqlsrv_get_field
bool past_next_result_end ; // core_sqlsrv_next_result sets this to true when the statement goes beyond the
// last results
unsigned long query_timeout ; // maximum allowed statement execution time
zend_long buffered_query_limit ; // maximum allowed memory for a buffered query (measured in KB)
// holds output pointers for SQLBindParameter
// We use a deque because it 1) provides the at/[] access in constant time, and 2) grows dynamically without moving
// memory, which is important because we pass the pointer to an element of the deque to SQLBindParameter to hold
std : : deque < SQLLEN > param_ind_ptrs ; // output pointers for lengths for calls to SQLBindParameter
zval param_input_strings ; // hold all UTF-16 input strings that aren't managed by PHP
zval output_params ; // hold all the output parameters
zval param_streams ; // track which streams to send data to the server
zval param_datetime_buffers ; // datetime strings to be converted back to DateTime objects
bool send_streams_at_exec ; // send all stream data right after execution before returning
sqlsrv_stream current_stream ; // current stream sending data to the server as an input parameter
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
2016-06-13 23:44:53 +02:00
zval active_stream ; // the currently active stream reading data from the database
2016-04-12 23:43:46 +02:00
sqlsrv_stmt ( sqlsrv_conn * c , SQLHANDLE handle , error_callback e , void * drv TSRMLS_DC ) ;
virtual ~ sqlsrv_stmt ( void ) ;
// driver specific conversion rules from a SQL Server/ODBC type to one of the SQLSRV_PHPTYPE_* constants
2016-08-24 23:28:20 +02:00
virtual sqlsrv_phptype sql_type_to_php_type ( SQLINTEGER sql_type , SQLUINTEGER size , bool prefer_string_to_stream , bool prefer_number_to_string = false ) = 0 ;
2016-04-12 23:43:46 +02:00
} ;
// *** field metadata struct ***
struct field_meta_data {
sqlsrv_malloc_auto_ptr < SQLCHAR > field_name ;
SQLSMALLINT field_name_len ;
SQLSMALLINT field_type ;
SQLULEN field_size ;
SQLULEN field_precision ;
SQLSMALLINT field_scale ;
SQLSMALLINT field_is_nullable ;
field_meta_data ( ) : field_name_len ( 0 ) , field_type ( 0 ) , field_size ( 0 ) , field_precision ( 0 ) ,
field_scale ( 0 ) , field_is_nullable ( 0 )
{
}
~ field_meta_data ( )
{
}
} ;
// *** statement constants ***
// unknown column size used by core_sqlsrv_bind_param when the user doesn't supply a value
const SQLULEN SQLSRV_UNKNOWN_SIZE = 0xffffffff ;
const int SQLSRV_DEFAULT_SIZE = - 1 ; // size given for an output parameter that doesn't really need one (e.g., int)
// uninitialized query timeout value
const unsigned int QUERY_TIMEOUT_INVALID = 0xffffffff ;
// special buffered query constant
const size_t SQLSRV_CURSOR_BUFFERED = 0xfffffffeUL ; // arbitrary number that doesn't map to any other SQL_CURSOR_* constant
// factory to create a statement
typedef sqlsrv_stmt * ( * driver_stmt_factory ) ( sqlsrv_conn * conn , SQLHANDLE h , error_callback e , void * drv TSRMLS_DC ) ;
// *** statement functions ***
sqlsrv_stmt * core_sqlsrv_create_stmt ( sqlsrv_conn * conn , driver_stmt_factory stmt_factory , HashTable * options_ht ,
const stmt_option valid_stmt_opts [ ] , error_callback const err , void * driver TSRMLS_DC ) ;
void core_sqlsrv_bind_param ( sqlsrv_stmt * stmt , SQLUSMALLINT param_num , SQLSMALLINT direction , zval * param_z ,
SQLSRV_PHPTYPE php_out_type , SQLSRV_ENCODING encoding , SQLSMALLINT sql_type , SQLULEN column_size ,
SQLSMALLINT decimal_digits TSRMLS_DC ) ;
void core_sqlsrv_execute ( sqlsrv_stmt * stmt TSRMLS_DC , const char * sql = NULL , int sql_len = 0 ) ;
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 ,
2016-07-06 20:47:05 +02:00
_Out_ void * & field_value , _Out_ SQLLEN * field_length , bool cache_field ,
_Out_ SQLSRV_PHPTYPE * sqlsrv_php_type_out TSRMLS_DC ) ;
2016-04-12 23:43:46 +02:00
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 ) ;
2016-05-04 05:05:41 +02:00
void core_sqlsrv_set_scrollable ( sqlsrv_stmt * stmt , unsigned long cursor_type TSRMLS_DC ) ;
2016-04-12 23:43:46 +02:00
void core_sqlsrv_set_query_timeout ( sqlsrv_stmt * stmt , long timeout TSRMLS_DC ) ;
void core_sqlsrv_set_query_timeout ( sqlsrv_stmt * stmt , zval * value_z TSRMLS_DC ) ;
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 ) ;
//*********************************************************************************************************************************
// Result Set
//*********************************************************************************************************************************
// Abstract the result set so that a result set can either be used as is from ODBC or buffered.
// This is not a complete abstraction of a result set. Only enough is abstracted to allow for
// information and capabilities normally not available when a result set is not buffered
// (e.g., forward only vs buffered means row count is available and cursor movement is possible).
// Otherwise, normal ODBC calls are still valid and should be used to get information about the
// result set (e.g., SQLNumResultCols).
struct sqlsrv_result_set {
sqlsrv_stmt * odbc ;
explicit sqlsrv_result_set ( sqlsrv_stmt * ) ;
virtual ~ sqlsrv_result_set ( void ) { }
virtual bool cached ( int field_index ) = 0 ;
virtual SQLRETURN fetch ( SQLSMALLINT fetch_orientation , SQLLEN fetch_offset TSRMLS_DC ) = 0 ;
virtual SQLRETURN get_data ( SQLUSMALLINT field_index , SQLSMALLINT target_type ,
2016-07-06 20:47:05 +02:00
_Out_ void * buffer , SQLLEN buffer_length , _Out_ SQLLEN * out_buffer_length ,
2016-04-12 23:43:46 +02:00
bool handle_warning TSRMLS_DC ) = 0 ;
virtual SQLRETURN get_diag_field ( SQLSMALLINT record_number , SQLSMALLINT diag_identifier ,
2016-07-06 20:47:05 +02:00
_Out_ SQLPOINTER diag_info_buffer , SQLSMALLINT buffer_length ,
_Out_ SQLSMALLINT * out_buffer_length TSRMLS_DC ) = 0 ;
2016-04-12 23:43:46 +02:00
virtual sqlsrv_error * get_diag_rec ( SQLSMALLINT record_number ) = 0 ;
virtual SQLLEN row_count ( TSRMLS_D ) = 0 ;
} ;
struct sqlsrv_odbc_result_set : public sqlsrv_result_set {
explicit sqlsrv_odbc_result_set ( sqlsrv_stmt * ) ;
virtual ~ sqlsrv_odbc_result_set ( void ) ;
virtual bool cached ( int field_index ) { return false ; }
virtual SQLRETURN fetch ( SQLSMALLINT fetch_orientation , SQLLEN fetch_offset TSRMLS_DC ) ;
virtual SQLRETURN get_data ( SQLUSMALLINT field_index , SQLSMALLINT target_type ,
2016-07-06 20:47:05 +02:00
_Out_ void * buffer , SQLLEN buffer_length , _Out_ SQLLEN * out_buffer_length ,
2016-04-12 23:43:46 +02:00
bool handle_warning TSRMLS_DC ) ;
virtual SQLRETURN get_diag_field ( SQLSMALLINT record_number , SQLSMALLINT diag_identifier ,
2016-07-06 20:47:05 +02:00
_Out_ SQLPOINTER diag_info_buffer , SQLSMALLINT buffer_length ,
_Out_ SQLSMALLINT * out_buffer_length TSRMLS_DC ) ;
2016-04-12 23:43:46 +02:00
virtual sqlsrv_error * get_diag_rec ( SQLSMALLINT record_number ) ;
virtual SQLLEN row_count ( TSRMLS_D ) ;
private :
// prevent invalid instantiations and assignments
sqlsrv_odbc_result_set ( void ) ;
sqlsrv_odbc_result_set ( sqlsrv_odbc_result_set & ) ;
sqlsrv_odbc_result_set & operator = ( sqlsrv_odbc_result_set & ) ;
} ;
struct sqlsrv_buffered_result_set : public sqlsrv_result_set {
struct meta_data {
SQLSMALLINT type ;
SQLSMALLINT c_type ; // convenience
SQLULEN offset ; // in bytes
SQLULEN length ; // in bytes
SQLSMALLINT scale ;
static const SQLULEN SIZE_UNKNOWN = 0 ;
} ;
// default maximum amount of memory that a buffered query can consume
# define INI_BUFFERED_QUERY_LIMIT_DEFAULT "10240" // default used by the php.ini settings
static const zend_long BUFFERED_QUERY_LIMIT_DEFAULT = 10240 ; // measured in KB
static const zend_long BUFFERED_QUERY_LIMIT_INVALID = 0 ;
explicit sqlsrv_buffered_result_set ( sqlsrv_stmt * odbc TSRMLS_DC ) ;
virtual ~ sqlsrv_buffered_result_set ( void ) ;
virtual bool cached ( int field_index ) { return true ; }
virtual SQLRETURN fetch ( SQLSMALLINT fetch_orientation , SQLLEN fetch_offset TSRMLS_DC ) ;
virtual SQLRETURN get_data ( SQLUSMALLINT field_index , SQLSMALLINT target_type ,
2016-07-06 20:47:05 +02:00
_Out_ void * buffer , SQLLEN buffer_length , _Out_ SQLLEN * out_buffer_length ,
2016-04-12 23:43:46 +02:00
bool handle_warning TSRMLS_DC ) ;
virtual SQLRETURN get_diag_field ( SQLSMALLINT record_number , SQLSMALLINT diag_identifier ,
2016-07-06 20:47:05 +02:00
_Out_ SQLPOINTER diag_info_buffer , SQLSMALLINT buffer_length ,
_Out_ SQLSMALLINT * out_buffer_length TSRMLS_DC ) ;
2016-04-12 23:43:46 +02:00
virtual sqlsrv_error * get_diag_rec ( SQLSMALLINT record_number ) ;
virtual SQLLEN row_count ( TSRMLS_D ) ;
// buffered result set specific
SQLSMALLINT column_count ( void )
{
return col_count ;
}
struct meta_data & col_meta_data ( SQLSMALLINT i )
{
return meta [ i ] ;
}
private :
// prevent invalid instantiations and assignments
sqlsrv_buffered_result_set ( void ) ;
sqlsrv_buffered_result_set ( sqlsrv_buffered_result_set & ) ;
sqlsrv_buffered_result_set & operator = ( sqlsrv_buffered_result_set & ) ;
HashTable * cache ; // rows of data kept in index based hash table
SQLSMALLINT col_count ; // number of columns in the current result set
meta_data * meta ; // metadata for fields in the cache
SQLLEN current ; // 1 based, 0 means before first row
sqlsrv_error_auto_ptr last_error ; // if an error occurred, it is kept here
SQLUSMALLINT last_field_index ; // the last field data retrieved from
SQLLEN read_so_far ; // position within string to read from (for partial reads of strings)
sqlsrv_malloc_auto_ptr < SQLCHAR > temp_string ; // temp buffer to hold a converted field while in use
SQLLEN temp_length ; // number of bytes in the temp conversion buffer
2016-07-06 20:47:05 +02:00
typedef SQLRETURN ( sqlsrv_buffered_result_set : : * conv_fn ) ( SQLSMALLINT field_index , _Out_ void * buffer , SQLLEN buffer_length ,
_Out_ SQLLEN * out_buffer_length ) ;
2016-04-12 23:43:46 +02:00
typedef std : : map < SQLINTEGER , std : : map < SQLINTEGER , conv_fn > > conv_matrix_t ;
// two dimentional sparse matrix that holds the [from][to] functions that do conversions
static conv_matrix_t conv_matrix ;
// string conversion functions
2016-07-06 20:47:05 +02:00
SQLRETURN binary_to_wide_string ( SQLSMALLINT field_index , _Out_ void * buffer , SQLLEN buffer_length ,
_Out_ SQLLEN * out_buffer_length ) ;
SQLRETURN binary_to_system_string ( SQLSMALLINT field_index , _Out_ void * buffer , SQLLEN buffer_length ,
_Out_ SQLLEN * out_buffer_length ) ;
SQLRETURN system_to_wide_string ( SQLSMALLINT field_index , _Out_ void * buffer , SQLLEN buffer_length ,
_Out_ SQLLEN * out_buffer_length ) ;
SQLRETURN to_binary_string ( SQLSMALLINT field_index , _Out_ void * buffer , SQLLEN buffer_length ,
_Out_ SQLLEN * out_buffer_length ) ;
SQLRETURN to_same_string ( SQLSMALLINT field_index , _Out_ void * buffer , SQLLEN buffer_length ,
_Out_ SQLLEN * out_buffer_length ) ;
SQLRETURN wide_to_system_string ( SQLSMALLINT field_index , _Out_ void * buffer , SQLLEN buffer_length ,
_Out_ SQLLEN * out_buffer_length ) ;
2016-04-12 23:43:46 +02:00
// long conversion functions
2016-07-06 20:47:05 +02:00
SQLRETURN to_long ( SQLSMALLINT field_index , _Out_ void * buffer , SQLLEN buffer_length , _Out_ SQLLEN * out_buffer_length ) ;
SQLRETURN long_to_system_string ( SQLSMALLINT field_index , _Out_ void * buffer , SQLLEN buffer_length ,
_Out_ SQLLEN * out_buffer_length ) ;
SQLRETURN long_to_wide_string ( SQLSMALLINT field_index , _Out_ void * buffer , SQLLEN buffer_length ,
_Out_ SQLLEN * out_buffer_length ) ;
SQLRETURN long_to_double ( SQLSMALLINT field_index , _Out_ void * buffer , SQLLEN buffer_length ,
_Out_ SQLLEN * out_buffer_length ) ;
2016-04-12 23:43:46 +02:00
// double conversion functions
2016-07-06 20:47:05 +02:00
SQLRETURN to_double ( SQLSMALLINT field_index , _Out_ void * buffer , SQLLEN buffer_length , _Out_ SQLLEN * out_buffer_length ) ;
SQLRETURN double_to_system_string ( SQLSMALLINT field_index , _Out_ void * buffer , SQLLEN buffer_length ,
_Out_ SQLLEN * out_buffer_length ) ;
SQLRETURN double_to_wide_string ( SQLSMALLINT field_index , _Out_ void * buffer , SQLLEN buffer_length ,
_Out_ SQLLEN * out_buffer_length ) ;
SQLRETURN double_to_long ( SQLSMALLINT field_index , _Out_ void * buffer , SQLLEN buffer_length ,
_Out_ SQLLEN * out_buffer_length ) ;
2016-04-12 23:43:46 +02:00
// string to number conversion functions
// Future: See if these can be converted directly to template member functions
2016-07-06 20:47:05 +02:00
SQLRETURN string_to_double ( SQLSMALLINT field_index , _Out_ void * buffer , SQLLEN buffer_length ,
_Out_ SQLLEN * out_buffer_length ) ;
SQLRETURN string_to_long ( SQLSMALLINT field_index , _Out_ void * buffer , SQLLEN buffer_length ,
_Out_ SQLLEN * out_buffer_length ) ;
SQLRETURN wstring_to_double ( SQLSMALLINT field_index , _Out_ void * buffer , SQLLEN buffer_length ,
_Out_ SQLLEN * out_buffer_length ) ;
SQLRETURN wstring_to_long ( SQLSMALLINT field_index , _Out_ void * buffer , SQLLEN buffer_length ,
_Out_ SQLLEN * out_buffer_length ) ;
2016-04-12 23:43:46 +02:00
// utility functions for conversions
unsigned char * get_row ( void ) ;
} ;
//*********************************************************************************************************************************
// Utility
//*********************************************************************************************************************************
// Simple macro to alleviate unused variable warnings. These are optimized out by the compiler.
// We use this since the unused variables are buried in the PHP_FUNCTION macro.
# define SQLSRV_UNUSED( var ) var;
// do a heap check in debug mode, but only print errors, not all of the allocations
# define MEMCHECK_SILENT 1
// utility functions shared by multiple callers across files
bool convert_string_from_utf16_inplace ( SQLSRV_ENCODING encoding , char * * string , SQLLEN & len ) ;
bool convert_zval_string_from_utf16 ( SQLSRV_ENCODING encoding , zval * value_z , SQLLEN & len ) ;
bool validate_string ( char * string , SQLLEN & len ) ;
bool convert_string_from_utf16 ( SQLSRV_ENCODING encoding , const wchar_t * inString , SQLINTEGER cchInLen , char * * outString , SQLLEN & cchOutLen ) ;
wchar_t * utf16_string_from_mbcs_string ( SQLSRV_ENCODING php_encoding , const char * mbcs_string ,
2016-07-06 20:47:05 +02:00
unsigned int mbcs_len , _Out_ unsigned int * utf16_len ) ;
2016-04-12 23:43:46 +02:00
//*********************************************************************************************************************************
// Error handling routines and Predefined Errors
//*********************************************************************************************************************************
enum SQLSRV_ERROR_CODES {
SQLSRV_ERROR_ODBC ,
SQLSRV_ERROR_DRIVER_NOT_INSTALLED ,
SQLSRV_ERROR_ZEND_HASH ,
SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE ,
SQLSRV_ERROR_INVALID_PARAMETER_SQLTYPE ,
SQLSRV_ERROR_INVALID_PARAMETER_ENCODING ,
SQLSRV_ERROR_INPUT_PARAM_ENCODING_TRANSLATE ,
SQLSRV_ERROR_OUTPUT_PARAM_ENCODING_TRANSLATE ,
SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE ,
SQLSRV_ERROR_ZEND_STREAM ,
SQLSRV_ERROR_INPUT_STREAM_ENCODING_TRANSLATE ,
SQLSRV_ERROR_UNKNOWN_SERVER_VERSION ,
SQLSRV_ERROR_FETCH_PAST_END ,
SQLSRV_ERROR_STATEMENT_NOT_EXECUTED ,
SQLSRV_ERROR_NO_FIELDS ,
SQLSRV_ERROR_INVALID_TYPE ,
SQLSRV_ERROR_FETCH_NOT_CALLED ,
SQLSRV_ERROR_NO_DATA ,
SQLSRV_ERROR_FIELD_ENCODING_TRANSLATE ,
SQLSRV_ERROR_ZEND_HASH_CREATE_FAILED ,
SQLSRV_ERROR_NEXT_RESULT_PAST_END ,
SQLSRV_ERROR_UID_PWD_BRACES_NOT_ESCAPED ,
SQLSRV_ERROR_INVALID_OPTION_TYPE_INT ,
SQLSRV_ERROR_INVALID_OPTION_TYPE_STRING ,
SQLSRV_ERROR_CONN_OPTS_WRONG_TYPE ,
SQLSRV_ERROR_INVALID_CONNECTION_KEY ,
SQLSRV_ERROR_MAX_PARAMS_EXCEEDED ,
SQLSRV_ERROR_INVALID_OPTION_KEY ,
SQLSRV_ERROR_INVALID_QUERY_TIMEOUT_VALUE ,
SQLSRV_ERROR_INVALID_OPTION_SCROLLABLE ,
SQLSRV_ERROR_QUERY_STRING_ENCODING_TRANSLATE ,
SQLSRV_ERROR_OUTPUT_PARAM_TRUNCATED ,
SQLSRV_ERROR_INPUT_OUTPUT_PARAM_TYPE_MATCH ,
SQLSRV_ERROR_DATETIME_CONVERSION_FAILED ,
SQLSRV_ERROR_STREAMABLE_TYPES_ONLY ,
SQLSRV_ERROR_STREAM_CREATE ,
SQLSRV_ERROR_MARS_OFF ,
SQLSRV_ERROR_FIELD_INDEX_ERROR ,
SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED ,
SQLSRV_ERROR_INVALID_BUFFER_LIMIT ,
// Driver specific error codes starts from here.
SQLSRV_ERROR_DRIVER_SPECIFIC = 1000 ,
} ;
// the message returned by ODBC Driver 11 for SQL Server
2016-06-13 23:44:53 +02:00
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 " } ;
2016-04-12 23:43:46 +02:00
// SQLSTATE for all internal errors
extern SQLCHAR IMSSP [ ] ;
// SQLSTATE for all internal warnings
extern SQLCHAR SSPWARN [ ] ;
// flags passed to sqlsrv_errors to filter its return values
enum error_handling_flags {
SQLSRV_ERR_ERRORS ,
SQLSRV_ERR_WARNINGS ,
SQLSRV_ERR_ALL
} ;
// *** internal error macros and functions ***
// call to retrieve an error from ODBC. This uses SQLGetDiagRec, so the
// errno is 1 based. It returns it as an array with 3 members:
// 1/SQLSTATE) sqlstate
// 2/code) driver specific error code
// 3/message) driver specific error message
// The fetch type determines if the indices are numeric, associative, or both.
2016-07-06 20:47:05 +02:00
bool core_sqlsrv_get_odbc_error ( sqlsrv_context & ctx , int record_number , _Out_ sqlsrv_error_auto_ptr & error ,
2016-04-12 23:43:46 +02:00
logging_severity severity TSRMLS_DC ) ;
// format and return a driver specfic error
void core_sqlsrv_format_driver_error ( sqlsrv_context & ctx , sqlsrv_error_const const * custom_error ,
sqlsrv_error_auto_ptr & formatted_error , logging_severity severity TSRMLS_DC , va_list * args ) ;
// return the message for the HRESULT returned by GetLastError. Some driver errors use this to
// return the Windows error, e.g, when a UTF-8 <-> UTF-16 conversion fails.
const char * get_last_error_message ( DWORD last_error = 0 ) ;
// a wrapper around FormatMessage that can take variadic args rather than a a va_arg pointer
2016-10-25 01:11:52 +02:00
DWORD core_sqlsrv_format_message ( char * output_buffer , unsigned output_len , const char * format , . . . ) ;
2016-04-12 23:43:46 +02:00
// convenience functions that overload either a reference or a pointer so we can use
// either in the CHECK_* functions.
inline bool call_error_handler ( sqlsrv_context & ctx , unsigned long sqlsrv_error_code TSRMLS_DC , bool warning , . . . )
{
va_list print_params ;
va_start ( print_params , warning ) ;
bool ignored = ctx . error_handler ( ) ( ctx , sqlsrv_error_code , warning TSRMLS_CC , & print_params ) ;
va_end ( print_params ) ;
return ignored ;
}
inline bool call_error_handler ( sqlsrv_context * ctx , unsigned long sqlsrv_error_code TSRMLS_DC , bool warning , . . . )
{
va_list print_params ;
va_start ( print_params , warning ) ;
bool ignored = ctx - > error_handler ( ) ( * ctx , sqlsrv_error_code , warning TSRMLS_CC , & print_params ) ;
va_end ( print_params ) ;
return ignored ;
}
// PHP equivalent of ASSERT. C asserts cause a dialog to show and halt the process which
// we don't want on a web server
# define SQLSRV_ASSERT( condition, msg, ...) if( !(condition)) DIE( msg, __VA_ARGS__ );
# if defined( PHP_DEBUG )
# define DEBUG_SQLSRV_ASSERT( condition, msg, ... ) \
if ( ! ( condition ) ) { \
DIE ( msg , __VA_ARGS__ ) ; \
}
# else
# define DEBUG_SQLSRV_ASSERT( condition, msg, ... ) ((void)0)
# endif
// check to see if the sqlstate is 01004, truncated field retrieved. Used for retrieving large fields.
inline bool is_truncated_warning ( SQLCHAR * state )
{
# if defined(ZEND_DEBUG)
if ( state = = NULL | | strlen ( reinterpret_cast < char * > ( state ) ) ! = 5 ) { \
DIE ( " Incorrect SQLSTATE given to is_truncated_warning. " ) ; \
}
# endif
return ( state [ 0 ] = = ' 0 ' & & state [ 1 ] = = ' 1 ' & & state [ 2 ] = = ' 0 ' & & state [ 3 ] = = ' 0 ' & & state [ 4 ] = = ' 4 ' ) ;
}
// Macros for handling errors. These macros are simplified if statements that take boilerplate
// code down to a single line to avoid distractions in the code.
# define CHECK_ERROR_EX( unique, condition, context, ssphp, ... ) \
bool flag # # unique = ( condition ) ; \
bool ignored # # unique = true ; \
if ( flag # # unique ) { \
ignored # # unique = call_error_handler ( context , ssphp TSRMLS_CC , /*warning*/ false , __VA_ARGS__ ) ; \
} \
if ( ! ignored # # unique )
# define CHECK_ERROR_UNIQUE( unique, condition, context, ssphp, ...) \
CHECK_ERROR_EX ( unique , condition , context , ssphp , __VA_ARGS__ )
# define CHECK_ERROR( condition, context, ... ) \
CHECK_ERROR_UNIQUE ( __COUNTER__ , condition , context , NULL , __VA_ARGS__ )
# define CHECK_CUSTOM_ERROR( condition, context, ssphp, ... ) \
CHECK_ERROR_UNIQUE ( __COUNTER__ , condition , context , ssphp , __VA_ARGS__ )
# define CHECK_SQL_ERROR( result, context, ... ) \
SQLSRV_ASSERT ( result ! = SQL_INVALID_HANDLE , " Invalid handle returned. " ) ; \
CHECK_ERROR ( result = = SQL_ERROR , context , __VA_ARGS__ )
# define CHECK_WARNING_AS_ERROR_UNIQUE( unique, condition, context, ssphp, ... ) \
bool ignored # # unique = true ; \
if ( condition ) { \
ignored # # unique = call_error_handler ( context , ssphp TSRMLS_CC , /*warning*/ true , __VA_ARGS__ ) ; \
} \
if ( ! ignored # # unique )
# define CHECK_SQL_WARNING_AS_ERROR( result, context, ... ) \
CHECK_WARNING_AS_ERROR_UNIQUE ( __COUNTER__ , ( result = = SQL_SUCCESS_WITH_INFO ) , context , SQLSRV_ERROR_ODBC , __VA_ARGS__ )
# define CHECK_SQL_WARNING( result, context, ... ) \
if ( result = = SQL_SUCCESS_WITH_INFO ) { \
( void ) call_error_handler ( context , NULL TSRMLS_CC , /*warning*/ true , __VA_ARGS__ ) ; \
}
# define CHECK_CUSTOM_WARNING_AS_ERROR( condition, context, ssphp, ... ) \
CHECK_WARNING_AS_ERROR_UNIQUE ( __COUNTER__ , condition , context , ssphp , __VA_ARGS__ )
# define CHECK_ZEND_ERROR( zr, ctx, error, ... ) \
CHECK_ERROR_UNIQUE ( __COUNTER__ , ( zr = = FAILURE ) , ctx , error , __VA_ARGS__ ) \
# define CHECK_SQL_ERROR_OR_WARNING( result, context, ... ) \
SQLSRV_ASSERT ( result ! = SQL_INVALID_HANDLE , " Invalid handle returned. " ) ; \
bool ignored = true ; \
if ( result = = SQL_ERROR ) { \
ignored = call_error_handler ( context , SQLSRV_ERROR_ODBC TSRMLS_CC , false , __VA_ARGS__ ) ; \
} \
else if ( result = = SQL_SUCCESS_WITH_INFO ) { \
ignored = call_error_handler ( context , SQLSRV_ERROR_ODBC TSRMLS_CC , true TSRMLS_CC , __VA_ARGS__ ) ; \
} \
if ( ! ignored )
// throw an exception after it has been hooked into the custom error handler
# define THROW_CORE_ERROR( ctx, custom, ... ) \
( void ) call_error_handler ( ctx , custom TSRMLS_CC , /*warning*/ false , __VA_ARGS__ ) ; \
throw core : : CoreException ( ) ;
//*********************************************************************************************************************************
// ODBC/Zend function wrappers
//*********************************************************************************************************************************
namespace core {
// base exception for the driver
struct CoreException : public std : : exception {
CoreException ( )
{
}
} ;
inline void check_for_mars_error ( sqlsrv_stmt * stmt , SQLRETURN r TSRMLS_DC )
{
// We check for the 'connection busy' error caused by having MultipleActiveResultSets off
// and return a more helpful message prepended to the ODBC errors if that error occurs
if ( ! SQL_SUCCEEDED ( r ) ) {
SQLCHAR err_msg [ SQL_MAX_MESSAGE_LENGTH + 1 ] ;
SQLSMALLINT len = 0 ;
SQLRETURN r = : : SQLGetDiagField ( stmt - > handle_type ( ) , stmt - > handle ( ) , 1 , SQL_DIAG_MESSAGE_TEXT ,
err_msg , SQL_MAX_MESSAGE_LENGTH , & len ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , stmt ) {
throw CoreException ( ) ;
}
2016-06-13 23:44:53 +02:00
std : : size_t driver_version = stmt - > conn - > driver_version ;
2016-07-06 20:47:05 +02:00
if ( ! strcmp ( reinterpret_cast < const char * > ( err_msg ) , CONNECTION_BUSY_ODBC_ERROR [ driver_version ] ) ) {
2016-04-12 23:43:46 +02:00
THROW_CORE_ERROR ( stmt , SQLSRV_ERROR_MARS_OFF ) ;
}
}
}
// *** ODBC wrappers ***
// wrap the ODBC functions to throw exceptions rather than use the return value to signal errors
// some of the signatures have been altered to be more convenient since the return value is no longer
// required to return the status of the call (e.g., SQLNumResultCols).
// These functions take the sqlsrv_context type. However, since the error handling code can alter
// the context to hold the error, they are not passed as const.
inline SQLRETURN SQLGetDiagField ( sqlsrv_context * ctx , SQLSMALLINT record_number , SQLSMALLINT diag_identifier ,
2016-07-06 20:47:05 +02:00
_Out_ SQLPOINTER diag_info_buffer , SQLSMALLINT buffer_length ,
_Out_ SQLSMALLINT * out_buffer_length TSRMLS_DC )
2016-04-12 23:43:46 +02:00
{
SQLRETURN r = : : SQLGetDiagField ( ctx - > handle_type ( ) , ctx - > handle ( ) , record_number , diag_identifier ,
diag_info_buffer , buffer_length , out_buffer_length ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , ctx ) {
throw CoreException ( ) ;
}
return r ;
}
inline void SQLAllocHandle ( SQLSMALLINT HandleType , sqlsrv_context & InputHandle ,
2016-07-06 20:47:05 +02:00
_Out_writes_ ( 1 ) SQLHANDLE * OutputHandlePtr TSRMLS_DC )
2016-04-12 23:43:46 +02:00
{
SQLRETURN r ;
r = : : SQLAllocHandle ( HandleType , InputHandle . handle ( ) , OutputHandlePtr ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , InputHandle ) {
throw CoreException ( ) ;
}
}
inline void SQLBindParameter ( sqlsrv_stmt * stmt ,
SQLUSMALLINT ParameterNumber ,
SQLSMALLINT InputOutputType ,
SQLSMALLINT ValueType ,
SQLSMALLINT ParameterType ,
SQLULEN ColumnSize ,
SQLSMALLINT DecimalDigits ,
2016-07-06 20:47:05 +02:00
_Inout_ SQLPOINTER ParameterValuePtr ,
2016-04-12 23:43:46 +02:00
SQLLEN BufferLength ,
2016-07-06 20:47:05 +02:00
_Inout_ SQLLEN * StrLen_Or_IndPtr
2016-04-12 23:43:46 +02:00
TSRMLS_DC )
{
SQLRETURN r ;
r = : : SQLBindParameter ( stmt - > handle ( ) , ParameterNumber , InputOutputType , ValueType , ParameterType , ColumnSize ,
DecimalDigits , ParameterValuePtr , BufferLength , StrLen_Or_IndPtr ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , stmt ) {
throw CoreException ( ) ;
}
}
inline void SQLColAttribute ( sqlsrv_stmt * stmt , SQLUSMALLINT field_index , SQLUSMALLINT field_identifier ,
2016-07-06 20:47:05 +02:00
_Out_ SQLPOINTER field_type_char , SQLSMALLINT buffer_length ,
_Out_ SQLSMALLINT * out_buffer_length , _Out_ SQLLEN * field_type_num TSRMLS_DC )
2016-04-12 23:43:46 +02:00
{
SQLRETURN r = : : SQLColAttribute ( stmt - > handle ( ) , field_index , field_identifier , field_type_char ,
buffer_length , out_buffer_length , field_type_num ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , stmt ) {
throw CoreException ( ) ;
}
}
2016-07-06 20:47:05 +02:00
inline void SQLDescribeCol ( sqlsrv_stmt * stmt , SQLSMALLINT colno , _Out_ SQLCHAR * col_name , SQLSMALLINT col_name_length ,
_Out_ SQLSMALLINT * col_name_length_out , SQLSMALLINT * data_type , _Out_ SQLULEN * col_size ,
_Out_ SQLSMALLINT * decimal_digits , _Out_ SQLSMALLINT * nullable TSRMLS_DC )
2016-04-12 23:43:46 +02:00
{
SQLRETURN r ;
r = : : SQLDescribeCol ( stmt - > handle ( ) , colno , col_name , col_name_length , col_name_length_out ,
data_type , col_size , decimal_digits , nullable ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , stmt ) {
throw CoreException ( ) ;
}
}
inline void SQLEndTran ( SQLSMALLINT handleType , sqlsrv_conn * conn , SQLSMALLINT completionType TSRMLS_DC )
{
SQLRETURN r = : : SQLEndTran ( handleType , conn - > handle ( ) , completionType ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , conn ) {
throw CoreException ( ) ;
}
}
// SQLExecDirect returns the status code since it returns either SQL_NEED_DATA or SQL_NO_DATA besides just errors/success
inline SQLRETURN SQLExecDirect ( sqlsrv_stmt * stmt , char * sql TSRMLS_DC )
{
SQLRETURN r = : : SQLExecDirect ( stmt - > handle ( ) , reinterpret_cast < SQLCHAR * > ( sql ) , SQL_NTS ) ;
check_for_mars_error ( stmt , r TSRMLS_CC ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , stmt ) {
throw CoreException ( ) ;
}
return r ;
}
inline SQLRETURN SQLExecDirectW ( sqlsrv_stmt * stmt , wchar_t * wsql TSRMLS_DC )
{
SQLRETURN r ;
r = : : SQLExecDirectW ( stmt - > handle ( ) , reinterpret_cast < SQLWCHAR * > ( wsql ) , SQL_NTS ) ;
check_for_mars_error ( stmt , r TSRMLS_CC ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , stmt ) {
throw CoreException ( ) ;
}
return r ;
}
// SQLExecute returns the status code since it returns either SQL_NEED_DATA or SQL_NO_DATA besides just errors/success
inline SQLRETURN SQLExecute ( sqlsrv_stmt * stmt TSRMLS_DC )
{
SQLRETURN r ;
r = : : SQLExecute ( stmt - > handle ( ) ) ;
check_for_mars_error ( stmt , r TSRMLS_CC ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , stmt ) {
throw CoreException ( ) ;
}
return r ;
}
inline SQLRETURN SQLFetchScroll ( sqlsrv_stmt * stmt , SQLSMALLINT fetch_orientation , SQLLEN fetch_offset TSRMLS_DC )
{
SQLRETURN r = : : SQLFetchScroll ( stmt - > handle ( ) , fetch_orientation , fetch_offset ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , stmt ) {
throw CoreException ( ) ;
}
return r ;
}
// wrap SQLFreeHandle and report any errors, but don't actually signal an error to the calling routine
inline void SQLFreeHandle ( sqlsrv_context & ctx TSRMLS_DC )
{
SQLRETURN r ;
r = : : SQLFreeHandle ( ctx . handle_type ( ) , ctx . handle ( ) ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , ctx ) { }
}
inline SQLRETURN SQLGetData ( sqlsrv_stmt * stmt , SQLUSMALLINT field_index , SQLSMALLINT target_type ,
2016-07-06 20:47:05 +02:00
_Out_ void * buffer , SQLLEN buffer_length , _Out_ SQLLEN * out_buffer_length ,
2016-04-12 23:43:46 +02:00
bool handle_warning TSRMLS_DC )
{
SQLRETURN r = : : SQLGetData ( stmt - > handle ( ) , field_index , target_type , buffer , buffer_length , out_buffer_length ) ;
if ( r = = SQL_NO_DATA )
return r ;
CHECK_SQL_ERROR ( r , stmt ) {
throw CoreException ( ) ;
}
if ( handle_warning ) {
CHECK_SQL_WARNING_AS_ERROR ( r , stmt ) {
throw CoreException ( ) ;
}
}
return r ;
}
2016-07-06 20:47:05 +02:00
inline void SQLGetInfo ( sqlsrv_conn * conn , SQLUSMALLINT info_type , _Out_ SQLPOINTER info_value , SQLSMALLINT buffer_len ,
_Out_ SQLSMALLINT * str_len TSRMLS_DC )
2016-04-12 23:43:46 +02:00
{
SQLRETURN r ;
r = : : SQLGetInfo ( conn - > handle ( ) , info_type , info_value , buffer_len , str_len ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , conn ) {
throw CoreException ( ) ;
}
}
inline void SQLGetTypeInfo ( sqlsrv_stmt * stmt , SQLUSMALLINT data_type TSRMLS_DC )
{
SQLRETURN r ;
r = : : SQLGetTypeInfo ( stmt - > handle ( ) , data_type ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , stmt ) {
throw CoreException ( ) ;
}
}
// SQLMoreResults returns the status code since it returns SQL_NO_DATA when there is no more data in a result set.
inline SQLRETURN SQLMoreResults ( sqlsrv_stmt * stmt TSRMLS_DC )
{
SQLRETURN r = : : SQLMoreResults ( stmt - > handle ( ) ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , stmt ) {
throw CoreException ( ) ;
}
return r ;
}
inline SQLSMALLINT SQLNumResultCols ( sqlsrv_stmt * stmt TSRMLS_DC )
{
SQLRETURN r ;
SQLSMALLINT num_cols ;
r = : : SQLNumResultCols ( stmt - > handle ( ) , & num_cols ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , stmt ) {
throw CoreException ( ) ;
}
return num_cols ;
}
// SQLParamData returns the status code since it returns either SQL_NEED_DATA or SQL_NO_DATA when there are more
// parameters or when the parameters are all processed.
2016-07-06 20:47:05 +02:00
inline SQLRETURN SQLParamData ( sqlsrv_stmt * stmt , _Out_ SQLPOINTER * value_ptr_ptr TSRMLS_DC )
2016-04-12 23:43:46 +02:00
{
SQLRETURN r ;
r = : : SQLParamData ( stmt - > handle ( ) , value_ptr_ptr ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , stmt ) {
throw CoreException ( ) ;
}
return r ;
}
inline void SQLPrepareW ( sqlsrv_stmt * stmt , SQLWCHAR * sql , SQLINTEGER sql_len TSRMLS_DC )
{
SQLRETURN r ;
r = : : SQLPrepareW ( stmt - > handle ( ) , sql , sql_len ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , stmt ) {
throw CoreException ( ) ;
}
}
inline void SQLPutData ( sqlsrv_stmt * stmt , SQLPOINTER data_ptr , SQLLEN strlen_or_ind TSRMLS_DC )
{
SQLRETURN r ;
r = : : SQLPutData ( stmt - > handle ( ) , data_ptr , strlen_or_ind ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , stmt ) {
throw CoreException ( ) ;
}
}
inline SQLLEN SQLRowCount ( sqlsrv_stmt * stmt TSRMLS_DC )
{
SQLRETURN r ;
SQLLEN rows_affected ;
r = : : SQLRowCount ( stmt - > handle ( ) , & rows_affected ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , stmt ) {
throw CoreException ( ) ;
}
return rows_affected ;
}
inline void SQLSetConnectAttr ( sqlsrv_context & ctx , SQLINTEGER attr , SQLPOINTER value_ptr , SQLINTEGER str_len TSRMLS_DC )
{
SQLRETURN r ;
r = : : SQLSetConnectAttr ( ctx . handle ( ) , attr , value_ptr , str_len ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , ctx ) {
throw CoreException ( ) ;
}
}
inline void SQLSetEnvAttr ( sqlsrv_context & ctx , SQLINTEGER attr , SQLPOINTER value_ptr , SQLINTEGER str_len TSRMLS_DC )
{
SQLRETURN r ;
r = : : SQLSetEnvAttr ( ctx . handle ( ) , attr , value_ptr , str_len ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , ctx ) {
throw CoreException ( ) ;
}
}
inline void SQLSetConnectAttr ( sqlsrv_conn * conn , SQLINTEGER attribute , SQLPOINTER value_ptr , SQLINTEGER value_len TSRMLS_DC )
{
SQLRETURN r = : : SQLSetConnectAttr ( conn - > handle ( ) , attribute , value_ptr , value_len ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , conn ) {
throw CoreException ( ) ;
}
}
inline void SQLSetStmtAttr ( sqlsrv_stmt * stmt , SQLINTEGER attr , SQLPOINTER value_ptr , SQLINTEGER str_len TSRMLS_DC )
{
SQLRETURN r ;
r = : : SQLSetStmtAttr ( stmt - > handle ( ) , attr , value_ptr , str_len ) ;
CHECK_SQL_ERROR_OR_WARNING ( r , stmt ) {
throw CoreException ( ) ;
}
}
// *** zend wrappers ***
2016-05-04 05:05:41 +02:00
//zend_resource_dtor sets the type of destroyed resources to -1
# define RSRC_INVALID_TYPE -1
2016-04-12 23:43:46 +02:00
// wrapper for ZVAL_STRINGL macro. ZVAL_STRINGL always allocates memory when initialzing new string from char string
// so allocated memory inside of value_z should be released before assigning it to the new string
inline void sqlsrv_zval_stringl ( zval * value_z , const char * str , const std : : size_t str_len )
{
if ( Z_TYPE_P ( value_z ) = = IS_STRING & & Z_STR_P ( value_z ) ! = NULL ) {
zend_string * temp_zstr = zend_string_init ( str , str_len , 0 ) ;
zend_string_release ( Z_STR_P ( value_z ) ) ;
ZVAL_NEW_STR ( value_z , temp_zstr ) ;
}
else {
ZVAL_STRINGL ( value_z , str , str_len ) ;
}
}
// exception thrown when a zend function wrapped here fails.
// wrappers for the zend functions called by our driver. These functions hook into the error reporting of our driver and throw
// exceptions when an error occurs. They are prefaced with sqlsrv_<zend_function_name> because many of the zend functions are
// actually macros that call other functions, so the sqlsrv_ is necessary to differentiate them from the macro system.
// If there is a zend function in the source that isn't found here, it is because it returns void and there is no error
// that can be thrown from it.
inline void sqlsrv_add_index_zval ( sqlsrv_context & ctx , zval * array , zend_ulong index , zval * value TSRMLS_DC )
{
int zr = : : add_index_zval ( array , index , value ) ;
CHECK_ZEND_ERROR ( zr , ctx , SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException ( ) ;
}
}
inline void sqlsrv_add_next_index_zval ( sqlsrv_context & ctx , zval * array , zval * value TSRMLS_DC )
{
int zr = : : add_next_index_zval ( array , value ) ;
CHECK_ZEND_ERROR ( zr , ctx , SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException ( ) ;
}
}
inline void sqlsrv_add_assoc_null ( sqlsrv_context & ctx , zval * array_z , const char * key TSRMLS_DC )
{
int zr = : : add_assoc_null ( array_z , key ) ;
CHECK_ZEND_ERROR ( zr , ctx , SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException ( ) ;
}
}
inline void sqlsrv_add_assoc_long ( sqlsrv_context & ctx , zval * array_z , const char * key , zend_long val TSRMLS_DC )
{
int zr = : : add_assoc_long ( array_z , key , val ) ;
CHECK_ZEND_ERROR ( zr , ctx , SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException ( ) ;
}
}
inline void sqlsrv_add_assoc_string ( sqlsrv_context & ctx , zval * array_z , const char * key , char * val , bool duplicate TSRMLS_DC )
{
int zr = : : add_assoc_string ( array_z , key , val ) ;
CHECK_ZEND_ERROR ( zr , ctx , SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException ( ) ;
}
if ( duplicate = = 0 ) {
sqlsrv_free ( val ) ;
}
}
2016-07-06 20:47:05 +02:00
inline void sqlsrv_array_init ( sqlsrv_context & ctx , _Out_ zval * new_array TSRMLS_DC )
2016-04-12 23:43:46 +02:00
{
int zr = : : array_init ( new_array ) ;
CHECK_ZEND_ERROR ( zr , ctx , SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException ( ) ;
}
}
inline void sqlsrv_php_stream_from_zval_no_verify ( sqlsrv_context & ctx , php_stream * & stream , zval * stream_z TSRMLS_DC )
{
// this duplicates the macro php_stream_from_zval_no_verify, which we can't use because it has an assignment
php_stream_from_zval_no_verify ( stream , stream_z ) ;
CHECK_CUSTOM_ERROR ( stream = = NULL , ctx , SQLSRV_ERROR_ZEND_STREAM ) {
throw CoreException ( ) ;
}
}
2016-07-06 20:47:05 +02:00
inline void sqlsrv_zend_hash_get_current_data ( sqlsrv_context & ctx , HashTable * ht , _Out_ zval * & output_data TSRMLS_DC )
2016-04-12 23:43:46 +02:00
{
int zr = ( output_data = : : zend_hash_get_current_data ( ht ) ) ! = NULL ? SUCCESS : FAILURE ;
CHECK_ZEND_ERROR ( zr , ctx , SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException ( ) ;
}
}
2016-07-06 20:47:05 +02:00
inline void sqlsrv_zend_hash_get_current_data_ptr ( sqlsrv_context & ctx , HashTable * ht , _Out_ void * & output_data TSRMLS_DC )
2016-04-12 23:43:46 +02:00
{
int zr = ( output_data = : : zend_hash_get_current_data_ptr ( ht ) ) ! = NULL ? SUCCESS : FAILURE ;
CHECK_ZEND_ERROR ( zr , ctx , SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException ( ) ;
}
}
inline void sqlsrv_zend_hash_index_del ( sqlsrv_context & ctx , HashTable * ht , zend_ulong index TSRMLS_DC )
{
int zr = : : zend_hash_index_del ( ht , index ) ;
CHECK_ZEND_ERROR ( zr , ctx , SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException ( ) ;
}
}
inline void sqlsrv_zend_hash_index_update ( sqlsrv_context & ctx , HashTable * ht , zend_ulong index , zval * data_z TSRMLS_DC )
{
int zr = ( data_z = : : zend_hash_index_update ( ht , index , data_z ) ) ! = NULL ? SUCCESS : FAILURE ;
CHECK_ZEND_ERROR ( zr , ctx , SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException ( ) ;
}
}
inline void sqlsrv_zend_hash_index_update_ptr ( sqlsrv_context & ctx , HashTable * ht , zend_ulong index , void * pData TSRMLS_DC )
{
int zr = ( pData = : : zend_hash_index_update_ptr ( ht , index , pData ) ) ! = NULL ? SUCCESS : FAILURE ;
CHECK_ZEND_ERROR ( zr , ctx , SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException ( ) ;
}
}
inline void sqlsrv_zend_hash_index_update_mem ( sqlsrv_context & ctx , HashTable * ht , zend_ulong index , void * pData , std : : size_t size TSRMLS_DC )
{
int zr = ( pData = : : zend_hash_index_update_mem ( ht , index , pData , size ) ) ! = NULL ? SUCCESS : FAILURE ;
CHECK_ZEND_ERROR ( zr , ctx , SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException ( ) ;
}
}
inline void sqlsrv_zend_hash_next_index_insert ( sqlsrv_context & ctx , HashTable * ht , zval * data TSRMLS_DC )
{
int zr = ( data = : : zend_hash_next_index_insert ( ht , data ) ) ! = NULL ? SUCCESS : FAILURE ;
CHECK_ZEND_ERROR ( zr , ctx , SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException ( ) ;
}
}
inline void sqlsrv_zend_hash_next_index_insert_mem ( sqlsrv_context & ctx , HashTable * ht , void * data , uint data_size TSRMLS_DC )
{
int zr = ( data = : : zend_hash_next_index_insert_mem ( ht , data , data_size ) ) ! = NULL ? SUCCESS : FAILURE ;
CHECK_ZEND_ERROR ( zr , ctx , SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException ( ) ;
}
}
inline void sqlsrv_zend_hash_next_index_insert_ptr ( sqlsrv_context & ctx , HashTable * ht , void * data TSRMLS_DC )
{
int zr = ( data = : : zend_hash_next_index_insert_ptr ( ht , data ) ) ! = NULL ? SUCCESS : FAILURE ;
CHECK_ZEND_ERROR ( zr , ctx , SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException ( ) ;
}
}
inline void sqlsrv_zend_hash_init ( sqlsrv_context & ctx , HashTable * ht , uint32_t initial_size ,
dtor_func_t dtor_fn , zend_bool persistent TSRMLS_DC )
{
: : zend_hash_init ( ht , initial_size , NULL , dtor_fn , persistent ) ;
}
inline void sqlsrv_zend_hash_add ( sqlsrv_context & ctx , HashTable * ht , zend_string * key , unsigned int key_len , zval * data ,
unsigned int data_size , zval * pDest TSRMLS_DC )
{
int zr = ( pDest = : : zend_hash_add ( ht , key , data ) ) ! = NULL ? SUCCESS : FAILURE ;
CHECK_ZEND_ERROR ( zr , ctx , SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException ( ) ;
}
}
template < typename Statement >
sqlsrv_stmt * allocate_stmt ( sqlsrv_conn * conn , SQLHANDLE h , error_callback e , void * driver TSRMLS_DC )
{
return new ( sqlsrv_malloc ( sizeof ( Statement ) ) ) Statement ( conn , h , e , driver TSRMLS_CC ) ;
}
template < typename Connection >
sqlsrv_conn * allocate_conn ( SQLHANDLE h , error_callback e , void * driver TSRMLS_DC )
{
return new ( sqlsrv_malloc ( sizeof ( Connection ) ) ) Connection ( h , e , driver TSRMLS_CC ) ;
}
} // namespace core
# endif // CORE_SQLSRV_H