Merging PHP 7 to master

This commit is contained in:
Meet Bhagdev 2016-09-01 17:13:33 -07:00
parent ff7d8a3c93
commit 26e9d4f7db
47 changed files with 4133 additions and 3596 deletions

View file

@ -1,4 +1,4 @@
Copyright(c) 2015 Microsoft Corporation
Copyright(c) 2016 Microsoft Corporation
All rights reserved.
MIT License

149
README.md
View file

@ -1,81 +1,130 @@
#Microsoft Drivers for PHP for SQL Server
# Microsoft Drivers for PHP for SQL Server
**Welcome to the Microsoft Drivers for PHP for SQL Server project!**
**Welcome to the Microsoft Drivers for PHP for SQL Server PHP 7**
**Note:** For the PHP 7 project, see the PHP7 branch
The Microsoft Drivers for PHP for SQL Server are PHP extensions that allow for the reading and writing of SQL Server data from within PHP scripts. The SQLSRV extension provides a procedural interface while the PDO_SQLSRV extension implements PDO for accessing data in all editions of SQL Server 2005 and later (including Azure SQL DB). These drivers rely on the Microsoft ODBC Driver for SQL Server to handle the low-level communication with SQL Server.
The Microsoft Drivers for PHP for SQL Server are PHP 5 extensions that allow for the reading and writing of SQL Server data from within PHP scripts. The release contains two drivers, the SQLSRV driver and the PDO_SQLSRV driver. The SQLSRV extension provides a procedural interface while the PDO_SQLSRV extension implements PDO for accessing data in all editions of SQL Server 2005 and later (including SQL Azure). These drivers rely on the Microsoft ODBC Driver 11 for SQL Server to handle the low-level communication with SQL Server.
We hope you enjoy using the Microsoft Drivers for PHP for SQL Server.
This release contains the SQLSRV and PDO_SQLSRV drivers for PHP 7 with improvements on both drivers and some limitations (see Limitations below for details). Upcoming release(s) will contain more functionality, bug fixes, and more (see Plans below for more details).
The Microsoft Drivers for PHP for SQL Server Team
Announcements
- **July 06, 2016:** PHP Driver 4.0 for SQL Server with PHP 7 support is now GA. You can get the binaries [HERE](https://github.com/Azure/msphpsql/releases) or download the exe from the [Microsoft Download Center](https://www.microsoft.com/en-us/download/details.aspx?id=20098)
- **Jan 29, 2016:** A PHP7 branch is now active for the Early Technical Preview supporting PHP 7
- Please visit the [blog][blog] for more announcements.
##Announcements
## Prerequisites
August 22, 2016 (4.1.1): Updated Windows drivers built and compiled with PHP 7.0.9 are available and include a couple of bug fixes:
You must first be able to build PHP without including these
extensions. For help with doing this, see the [official PHP website][phpweb].
- Fixed issue with storing integers in varchar field.
- Fixed issue with invalid connection handler if one connection fails.
- Fixed crash when emulate prepare is on.
July 28, 2016 (4.1.0): Thanks to the community's input, this release expands drivers functionalities and also includes some bug fixes:
- `SQLSRV_ATTR_FETCHES_NUMERIC_TYPE` connection attribute flag is added to PDO_SQLSRV driver to handle numeric fetches from columns with numeric Sql types (only bit, integer, smallint, tinyint, float and real). This flag can be turned on by setting its value in `PDO::setAttribute` to `true`, For example,
`$conn->setAttribute(PDO::SQLSRV_ATTR_FETCHES_NUMERIC_TYPE,true);`
If `SQLSRV_ATTR_FETCHES_NUMERIC_TYPE` is set to `true` the results from an integer column will be represented as an `int`, likewise, Sql types float and real will be represented as `float`.
Note for exceptions:
- When connection option flag `ATTR_STRINGIFY_FETCHES` is on, even when `SQLSRV_ATTR_FETCHES_NUMERIC_TYPE` is on, the return value will still be string.
- When the returned PDO type in bind column is `PDO_PARAM_INT`, the return value from a integer column will be int even if `SQLSRV_ATTR_FETCHES_NUMERIC_TYPE` is off.
- Fixed float truncation when using buffered query.
- Fixed handling of Unicode strings and binary when emulate prepare is on in `PDOStatement::bindParam`. To bind a unicode string, `PDO::SQLSRV_ENCODING_UTF8` should be set using `$driverOption`, and to bind a string to column of Sql type binary, `PDO::SQLSRV_ENCODING_BINARY` should be set.
- Fixed string truncation in bind output parameters when the size is not set and the length of initialized variable is less than the output.
- Fixed bind string parameters as bidirectional parameters (`PDO::PARAM_INPUT_OUTPUT `) in PDO_SQLSRV driver. Note for output or bidirectional parameters, `PDOStatement::closeCursor` should be called to get the output value.
## Build
To compile the SQLSRV and PDO_SQLSRV:
Note: if you prefer, you can use the pre-compiled binary found [HERE](https://github.com/Azure/msphpsql/releases)
1. Copy the source code directories from this repository into the ext
subdirectory.
####Prerequisites
2. Run buildconf.bat to rebuild the configure.js script to include the
new drivers.
You must first be able to build PHP 7 without including these extensions. For help with doing this, see the [official PHP website][phpbuild] for building your own PHP on Windows.
3. Run "cscript configure.js --enable-sqlsrv=shared --enable-pdo
--with-pdo-sqlsrv=shared [other options]" to generate the makefile.
Run "cscript configure.js --help" to see what other options are
available. It is possible (and even probable) that other extensions
will have to be disabled or enabled for the compile to succeed.
Search bing.com for configurations that have worked for other people.
* It might be possible to compile these extensions as non-shared but that configuration has not been tested.
* NB: To build the driver with PHP 5.6.7 and later, you will need to specify the --with-odbcver=0x0380 argument to configure.js
####Compile the drivers
4. Run "nmake". It is suggested that you run the entire build. If you
wish to do so, run "nmake clean" first.
1. Copy the sqlsrv and/or pdo_sqlsrv source code directory from this repository into the ext subdirectory.
5. To install the resulting build, run "nmake install" or just copy
php_sqlsrv.dll and php_pdo_sqlsrv.dll to your PHP extension directory.
Also enable them within your PHP installation's php.ini file.
2. Run `buildconf.bat` to rebuild the configure.js script to include the driver.
This software has been compiled and tested under PHP 5.4.32 and later
using the Visual C++ 2008 and 2012, Express and Standard compilers.
3. Run `configure.bat --with-odbcver=0x0380 and the desired driver options (as below) [plus other options such as --disable-zts for the Non Thread Safe build]` to generate the makefile. You can run `configure.bat --help` to see what other options are available.
* For SQLSRV use: `--enable-sqlsrv=shared`
* For PDO_SQLSRV use: `--enable-pdo=shared --with-pdo-sqlsrv=shared`
## Documentation
4. Run `nmake`. It is suggested that you run the entire build. If you wish to do so, run `nmake clean` first.
This driver is documented on [Microsoft's Documentation web site][phpdoc].
5. To install the resulting build, run `nmake install` or just copy php_sqlsrv.dll and/or php_pdo_sqlsrv.dll to your PHP extension directory.
## Changes
This software has been compiled and tested under PHP 7.0.8 using the Visual C++ 2015 compiler.
For details about the changes included in this release, please see our [blog][blog] or see the SQLSRV_Readme.htm
file that is part of the download package.
## Install
####Prerequisites
- A Web server such as Internet Information Services (IIS) is required. Your Web server must be configured to run PHP
- [Microsoft ODBC Driver 11][odbc11] or [Microsoft ODBC Driver 13][odbc13]
####Enable the drivers
1. Make sure that the driver is in your PHP extension directory (you can simply copy it there if you did not use nmake install).
2. Enable it within your PHP installation's php.ini: `extension=php_sqlsrv.dll` and/or `extension=php_pdo_sqlsrv.dll`. If necessary, specify the extension directory using extension_dir, for example: `extension_dir = "C:\PHP\ext"`
3. Restart the Web server.
## Sample Code
For samples, please see the sample folder. For setup instructions, see [here] [phpazure]
## Limitations
- This release contains the PHP 7 port of the SQLSRV and PDO_SQLSRV drivers, and does not provide backwards compatibility with PHP 5.
- Binding output parameter using emulate prepare is not supported.
## Known Issues
- User defined data types and SQL_VARIANT.
Please visit the [project on Github][project] to view outstanding [issues][issues].
## Future Plans
- Expand SQL 16 Feature Support (example: Always Encrypted)
- Build Verification/Fundamental Tests
- Bug Fixes
## Download the driver
The driver can be downloaded from the [Microsoft Download Center][link]
## Guidelines for Reporting Issues
We appreciate you taking the time to test the driver, provide feedback and report any issues. It would be extremely helpful if you:
## Notes
- Report each issue as a new issue (but check first if it's already been reported)
- Try to be detailed in your report. Useful information for good bug reports include:
* What you are seeing and what the expected behaviour is
* Which driver: SQLSRV or PDO_SQLSRV?
* Environment details: e.g. PHP version, thread safe (TS) or non-thread safe (NTS), 32-bit &/or 64-bit?
* Table schema (for some issues the data types make a big difference!)
* Any other relevant information you want to share
- Try to include a PHP script demonstrating the isolated problem.
Thank you!
## FAQs
**Q:** Can we get dates for any of the Future Plans listed above?
**A:** At this time, Microsoft is not able to announce dates. We are working extremely hard to release future versions of the driver. We will share future plans once they solidify over the next few weeks.
**Q:** What's next?
**A:** On Jan 29, 2016 we released an early technical preview for our PHP Driver and several since. We will continue to release frequently to improve the quality of our driver.
**Q:** Is Microsoft taking pull requests for this project?
**A:** We will not be seeking to take pull requests until GA, Build Verification, and Fundamental tests are released. At this point Microsoft will also begin actively developing using this GitHub project as the prime repository.
####Note about version.h
The version numbers in version.h in the source do not match the
version numbers in the supported PHP extension.
## License
The Microsoft Drivers for PHP for SQL Server are licensed under the MIT license. See the LICENSE file for more details.
## Resources
**Documentation**: [MSDN Online Documentation][phpdoc]. Please note that this documentation is not yet updated for PHP 7.
**Team Blog**: Browse our blog for comments and announcements from the team in the [team blog][blog].
**Known Issues**: Please visit the [project on Github][project] to view outstanding [issues][issues] and report new ones.
[blog]: http://blogs.msdn.com/b/sqlphp/
[project]: https://github.com/Azure/msphpsql
@ -84,6 +133,14 @@ The Microsoft Drivers for PHP for SQL Server are licensed under the MIT license.
[phpweb]: http://php.net
[phpbuild]: https://wiki.php.net/internals/windows/stepbystepbuild
[phpdoc]: http://msdn.microsoft.com/en-us/library/dd903047%28SQL.11%29.aspx
[link]: https://www.microsoft.com/en-us/download/details.aspx?id=20098
[odbc11]: https://www.microsoft.com/en-us/download/details.aspx?id=36434
[odbc13]: https://www.microsoft.com/en-us/download/details.aspx?id=50420
[phpazure]: https://azure.microsoft.com/en-us/documentation/articles/sql-database-develop-php-simple-windows/
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1 +1 @@
Microsoft Drivers 3.2.0 for PHP for SQL Server (PDO driver)
Microsoft Drivers 4.1 for PHP for SQL Server (PDO driver)

View file

@ -3,7 +3,7 @@
//
// Contents: JScript build configuration used by buildconf.bat
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.0 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -21,10 +21,13 @@ ARG_WITH("pdo-sqlsrv", "enable Microsoft Drivers for PHP for SQL Server (PDO dri
if( PHP_PDO_SQLSRV != "no" ) {
pdo_sqlsrv_src = "pdo_dbh.cpp pdo_init.cpp pdo_stmt.cpp pdo_util.cpp pdo_parser.cpp core_init.cpp core_conn.cpp core_stmt.cpp core_util.cpp core_stream.cpp core_results.cpp";
if (CHECK_LIB("odbc32.lib", "pdo_sqlsrv") && CHECK_LIB("odbccp32.lib", "pdo_sqlsrv") &&
CHECK_LIB("version.lib", "pdo_sqlsrv") && CHECK_LIB("psapi.lib", "pdo_sqlsrv")) {
EXTENSION("pdo_sqlsrv", "pdo_dbh.cpp pdo_init.cpp pdo_stmt.cpp pdo_util.cpp pdo_parser.cpp core_init.cpp core_conn.cpp core_stmt.cpp core_util.cpp core_stream.cpp core_results.cpp" )
EXTENSION("pdo_sqlsrv", pdo_sqlsrv_src, PHP_PDO_SQLSRV_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1")
CHECK_HEADER_ADD_INCLUDE('sql.h', 'CFLAGS_PDO_SQLSRV_ODBC');
CHECK_HEADER_ADD_INCLUDE('sqlext.h', 'CFLAGS_PDO_SQLSRV_ODBC');
ADD_FLAG( 'LDFLAGS_PDO_SQLSRV', '/NXCOMPAT /DYNAMICBASE /debug' );
@ -32,7 +35,6 @@ if( PHP_PDO_SQLSRV != "no" ) {
ADD_FLAG( 'CFLAGS_PDO_SQLSRV', '/GS' );
ADD_FLAG( 'CFLAGS_PDO_SQLSRV', '/Zi' );
ADD_FLAG( 'CFLAGS_PDO_SQLSRV', '/D ZEND_WIN32_FORCE_INLINE' );
ADD_FLAG( 'CFLAGS_PDO_SQLSRV', '/D _HAS_CPP0X=0' );
ADD_EXTENSION_DEP('pdo_sqlsrv', 'pdo');
}

View file

@ -3,7 +3,7 @@
//
// Contents: Core routines that use connection handles shared between sqlsrv and pdo_sqlsrv
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -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}";
@ -57,12 +57,12 @@ const char CONNECTION_OPTION_MARS_ON[] = "MARS_Connection={Yes};";
void build_connection_string_and_set_conn_attr( sqlsrv_conn* conn, const char* server, const char* uid, const char* pwd,
HashTable* options_ht, const connection_option valid_conn_opts[],
void* driver,__inout std::string& connection_string TSRMLS_DC );
void* driver,_Inout_ std::string& connection_string TSRMLS_DC );
void determine_server_version( sqlsrv_conn* conn TSRMLS_DC );
const char* get_processor_arch( void );
void get_server_version( sqlsrv_conn* conn, char** server_version, SQLSMALLINT& len TSRMLS_DC );
connection_option const* get_connection_option( sqlsrv_conn* conn, const char* key, unsigned int key_len TSRMLS_DC );
void common_conn_str_append_func( const char* odbc_name, const char* val, int val_len, std::string& conn_str TSRMLS_DC );
connection_option const* get_connection_option( sqlsrv_conn* conn, const char* key, SQLULEN key_len TSRMLS_DC );
void common_conn_str_append_func( const char* odbc_name, const char* val, size_t val_len, std::string& conn_str TSRMLS_DC );
}
@ -104,14 +104,14 @@ sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_
if( options_ht && zend_hash_num_elements( options_ht ) > 0 ) {
zval** option_zz = NULL;
zval* option_z = NULL;
int zr = SUCCESS;
zr = zend_hash_index_find( options_ht, SQLSRV_CONN_OPTION_CONN_POOLING, reinterpret_cast<void**>( &option_zz ));
if( zr != FAILURE ) {
option_z = zend_hash_index_find(options_ht, SQLSRV_CONN_OPTION_CONN_POOLING);
if (option_z) {
// if the option was found and it's not true, then use the non pooled environment handle
if(( Z_TYPE_PP( option_zz ) == IS_STRING && !core_str_zval_is_true( *option_zz )) || !zend_is_true( *option_zz ) ) {
if(( Z_TYPE_P( option_z ) == IS_STRING && !core_str_zval_is_true( option_z )) || !zend_is_true( option_z ) ) {
henv = &henv_ncp;
}
@ -124,38 +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 = (conn_str.length() + 1) * sizeof( wchar_t );
wconn_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_UTF8, conn_str.c_str(), 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();
// 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 );
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();
}
}
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();
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();
}
@ -167,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() );
@ -318,7 +329,7 @@ void core_sqlsrv_close( sqlsrv_conn* conn TSRMLS_DC )
// sql - T-SQL command to prepare
// sql_len - length of the T-SQL string
void core_sqlsrv_prepare( sqlsrv_stmt* stmt, const char* sql, long sql_len TSRMLS_DC )
void core_sqlsrv_prepare( sqlsrv_stmt* stmt, const char* sql, SQLLEN sql_len TSRMLS_DC )
{
try {
@ -333,10 +344,17 @@ void core_sqlsrv_prepare( sqlsrv_stmt* stmt, const char* sql, long sql_len TSRML
wsql_len = 0;
}
else {
if (sql_len > INT_MAX)
{
LOG(SEV_ERROR, "Convert input parameter to utf16: buffer length exceeded.");
throw core::CoreException();
}
SQLSRV_ENCODING encoding = (( stmt->encoding() == SQLSRV_ENCODING_DEFAULT ) ? stmt->conn->encoding() :
stmt->encoding() );
wsql_string = utf16_string_from_mbcs_string( encoding, reinterpret_cast<const char*>( sql ),
sql_len, &wsql_len );
static_cast<int>( sql_len ), &wsql_len );
CHECK_CUSTOM_ERROR( wsql_string == NULL, stmt, SQLSRV_ERROR_QUERY_STRING_ENCODING_TRANSLATE,
get_last_error_message() ) {
throw core::CoreException();
@ -359,7 +377,7 @@ void core_sqlsrv_prepare( sqlsrv_stmt* stmt, const char* sql, long sql_len TSRML
// conn - The connection resource by which the client and server are connected.
// *server_version - zval for returning results.
void core_sqlsrv_get_server_version( sqlsrv_conn* conn, __out zval *server_version TSRMLS_DC )
void core_sqlsrv_get_server_version( sqlsrv_conn* conn, _Out_ zval *server_version TSRMLS_DC )
{
try {
@ -367,7 +385,10 @@ void core_sqlsrv_get_server_version( sqlsrv_conn* conn, __out zval *server_versi
SQLSMALLINT buffer_len = 0;
get_server_version( conn, &buffer, buffer_len TSRMLS_CC );
ZVAL_STRINGL( server_version, buffer, buffer_len, 0 );
core::sqlsrv_zval_stringl( server_version, buffer, buffer_len );
if (NULL != buffer) {
sqlsrv_free( buffer );
}
buffer.transferred();
}
@ -383,7 +404,7 @@ void core_sqlsrv_get_server_version( sqlsrv_conn* conn, __out zval *server_versi
// conn - The connection resource by which the client and server are connected.
// *server_info - zval for returning results.
void core_sqlsrv_get_server_info( sqlsrv_conn* conn, __out zval *server_info TSRMLS_DC )
void core_sqlsrv_get_server_info( sqlsrv_conn* conn, _Out_ zval *server_info TSRMLS_DC )
{
try {
@ -422,7 +443,7 @@ void core_sqlsrv_get_server_info( sqlsrv_conn* conn, __out zval *server_info TSR
// conn - The connection resource by which the client and server are connected.
// *client_info - zval for returning the results.
void core_sqlsrv_get_client_info( sqlsrv_conn* conn, __out zval *client_info TSRMLS_DC )
void core_sqlsrv_get_client_info( sqlsrv_conn* conn, _Out_ zval *client_info TSRMLS_DC )
{
try {
@ -463,7 +484,7 @@ void core_sqlsrv_get_client_info( sqlsrv_conn* conn, __out zval *client_info TSR
// Properly escaped means that any '}' should be escaped by a prior '}'. It is assumed that
// the value will be surrounded by { and } by the caller after it has been validated
bool core_is_conn_opt_value_escaped( const char* value, int value_len )
bool core_is_conn_opt_value_escaped( const char* value, size_t value_len )
{
// if the value is already quoted, then only analyse the part inside the quotes and return it as
// unquoted since we quote it when adding it to the connection string.
@ -472,7 +493,7 @@ bool core_is_conn_opt_value_escaped( const char* value, int value_len )
value_len -= 2;
}
// check to make sure that all right braces are escaped
int i = 0;
size_t i = 0;
while( ( value[i] != '}' || ( value[i] == '}' && value[i+1] == '}' )) && i < value_len ) {
// skip both braces
if( value[i] == '}' )
@ -491,7 +512,7 @@ bool core_is_conn_opt_value_escaped( const char* value, int value_len )
namespace {
connection_option const* get_connection_option( sqlsrv_conn* conn, unsigned long key,
connection_option const* get_connection_option( sqlsrv_conn* conn, SQLULEN key,
const connection_option conn_opts[] TSRMLS_DC )
{
for( int opt_idx = 0; conn_opts[ opt_idx ].conn_option_key != SQLSRV_CONN_OPTION_INVALID; ++opt_idx ) {
@ -513,7 +534,7 @@ connection_option const* get_connection_option( sqlsrv_conn* conn, unsigned long
void build_connection_string_and_set_conn_attr( sqlsrv_conn* conn, const char* server, const char* uid, const char* pwd,
HashTable* options, const connection_option valid_conn_opts[],
void* driver,__inout std::string& connection_string TSRMLS_DC )
void* driver,_Inout_ std::string& connection_string TSRMLS_DC )
{
bool credentials_mentioned = false;
bool mars_mentioned = false;
@ -521,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 );
@ -566,40 +585,34 @@ void build_connection_string_and_set_conn_attr( sqlsrv_conn* conn, const char* s
// flag is set to false.
if( zend_hash_index_exists( options, SQLSRV_CONN_OPTION_TRACE_FILE )) {
zval** trace_value = NULL;
int zr = zend_hash_index_find( options, SQLSRV_CONN_OPTION_TRACE_ON, (void**)&trace_value );
zval* trace_value = NULL;
trace_value = zend_hash_index_find(options, SQLSRV_CONN_OPTION_TRACE_ON);
if( zr == FAILURE || !zend_is_true( *trace_value )) {
if (trace_value == NULL || !zend_is_true(trace_value)) {
zend_hash_index_del( options, SQLSRV_CONN_OPTION_TRACE_FILE );
}
}
for( zend_hash_internal_pointer_reset( options );
zend_hash_has_more_elements( options ) == SUCCESS;
zend_hash_move_forward( options )) {
int type = HASH_KEY_NON_EXISTANT;
char *key = NULL;
unsigned int key_len = -1;
unsigned long index = -1;
zval** data = NULL;
zend_string *key = NULL;
zend_ulong index = -1;
zval* data = NULL;
type = zend_hash_get_current_key_ex( options, &key, &key_len, &index, 0, NULL );
// 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, (void**) &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 ) {
@ -676,7 +689,7 @@ void determine_server_version( sqlsrv_conn* conn TSRMLS_DC )
errno = 0;
char version_major_str[ 3 ];
SERVER_VERSION version_major;
memcpy( version_major_str, p, 2 );
memcpy_s( version_major_str, sizeof( version_major_str ), p, 2 );
version_major_str[ 2 ] = '\0';
version_major = static_cast<SERVER_VERSION>( atoi( version_major_str ));
@ -690,7 +703,7 @@ void determine_server_version( sqlsrv_conn* conn TSRMLS_DC )
conn->server_version = version_major;
}
void common_conn_str_append_func( const char* odbc_name, const char* val, int val_len, std::string& conn_str TSRMLS_DC )
void common_conn_str_append_func( const char* odbc_name, const char* val, size_t val_len, std::string& conn_str TSRMLS_DC )
{
// wrap a connection option in a quote. It is presumed that any character that need to be escaped will
// be escaped, such as a closing }.
@ -713,7 +726,7 @@ void conn_str_append_func::func( connection_option const* option, zval* value, s
TSRMLS_DC )
{
const char* val_str = Z_STRVAL_P( value );
int val_len = Z_STRLEN_P( value );
size_t val_len = Z_STRLEN_P( value );
common_conn_str_append_func( option->odbc_name, val_str, val_len, conn_str TSRMLS_CC );
}
@ -729,15 +742,15 @@ void conn_null_func::func( connection_option const* /*option*/, zval* /*value*/,
// Values = ("true" or "1") are treated as true values. Everything else is treated as false.
// Returns 1 for true and 0 for false.
int core_str_zval_is_true( zval* value_z )
size_t core_str_zval_is_true( zval* value_z )
{
SQLSRV_ASSERT( Z_TYPE_P( value_z ) == IS_STRING, "core_str_zval_is_true: This function only accepts zval of type string." );
char* value_in = Z_STRVAL_P( value_z );
int val_len = Z_STRLEN_P( value_z );
size_t val_len = Z_STRLEN_P( value_z );
// strip any whitespace at the end (whitespace is the same value in ASCII and UTF-8)
int last_char = val_len - 1;
size_t last_char = val_len - 1;
while( isspace( value_in[ last_char ] )) {
value_in[ last_char ] = '\0';
val_len = last_char;
@ -745,7 +758,7 @@ int core_str_zval_is_true( zval* value_z )
}
// save adjustments to the value made by stripping whitespace at the end
ZVAL_STRINGL( value_z, value_in, val_len, 0 );
Z_STRLEN_P( value_z ) = val_len;
const char VALID_TRUE_VALUE_1[] = "true";
const char VALID_TRUE_VALUE_2[] = "1";

View file

@ -3,7 +3,7 @@
//
// Contents: common initialization routines shared by PDO and sqlsrv
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -37,8 +37,8 @@ OSVERSIONINFO g_osversion;
// err - Driver specific error handler which handles any errors during initialization.
void core_sqlsrv_minit( sqlsrv_context** henv_cp, sqlsrv_context** henv_ncp, error_callback err, const char* driver_func TSRMLS_DC )
{
SQLSRV_STATIC_ASSERT( sizeof( sqlsrv_sqltype ) == sizeof( long ));
SQLSRV_STATIC_ASSERT( sizeof( sqlsrv_phptype ) == sizeof( long ));
SQLSRV_STATIC_ASSERT( sizeof( sqlsrv_sqltype ) == sizeof( zend_long ) );
SQLSRV_STATIC_ASSERT( sizeof( sqlsrv_phptype ) == sizeof( zend_long ));
*henv_cp = *henv_ncp = SQL_NULL_HANDLE; // initialize return values to NULL
@ -146,11 +146,13 @@ void core_sqlsrv_mshutdown( sqlsrv_context& henv_cp, sqlsrv_context& henv_ncp )
henv_ncp.invalidate();
}
delete &henv_ncp;
if( henv_cp != SQL_NULL_HANDLE ) {
henv_cp.invalidate();
}
delete &henv_cp;
return;
}

View file

@ -3,7 +3,7 @@
//
// Contents: Result sets
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -76,19 +76,37 @@ bool get_bit( void* ptr, unsigned int bit )
// read in LOB field during buffered result creation
SQLPOINTER read_lob_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_buffered_result_set::meta_data& meta,
unsigned long mem_used TSRMLS_DC );
zend_long mem_used TSRMLS_DC );
// dtor for each row in the cache
void cache_row_dtor( void* data );
void cache_row_dtor(zval* data);
// convert a number to a string using locales
// There is an extra copy here, but given the size is short (usually <20 bytes) and the complications of
// subclassing a new streambuf just to avoid the copy, it's easier to do the copy
template <typename Char, typename Number>
SQLRETURN number_to_string( Number* number_data, __out void* buffer, SQLLEN buffer_length, __out SQLLEN* out_buffer_length,
SQLRETURN number_to_string( Number* number_data, _Out_ void* buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length,
sqlsrv_error_auto_ptr& last_error )
{
// get to display size by removing the null terminator from buffer length
size_t display_size = ( buffer_length - sizeof( Char )) / sizeof( Char );
std::basic_ostringstream<Char> os;
// use the display size to determine the sql type. And if it is a double, set the precision accordingly
// the display sizes are set by the ODBC driver based on the precision of the sql type
// otherwise we can just use the default precision as long will not be truncated
size_t real_display_size = 14;
size_t float_display_size = 24;
size_t real_precision = 7;
size_t float_precision = 15;
// this is the case of sql type float(24) or real
if ( display_size == real_display_size ) {
os.precision( real_precision );
}
// this is the case of sql type float(53)
else if ( display_size == float_display_size ) {
os.precision( float_precision );
}
std::locale loc;
os.imbue( loc );
std::use_facet< std::num_put< Char > >( loc ).put( std::basic_ostream<Char>::_Iter( os.rdbuf() ), os, ' ', *number_data );
@ -100,21 +118,21 @@ SQLRETURN number_to_string( Number* number_data, __out void* buffer, SQLLEN buff
return SQL_ERROR;
}
if( str_num.size() * sizeof(Char) + sizeof(Char) > (size_t) buffer_length ) {
if( str_num.size() * sizeof(Char) > (size_t) buffer_length ) {
last_error = new ( sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error(
(SQLCHAR*) "HY090", (SQLCHAR*) "Buffer length too small to hold number as string", -1 );
return SQL_ERROR;
}
*out_buffer_length = str_num.size() * sizeof(Char) + sizeof(Char); // include NULL terminator
memcpy( buffer, str_num.c_str(), *out_buffer_length );
*out_buffer_length = str_num.size() * sizeof( Char ); // str_num.size() already include the NULL terminator
memcpy_s( buffer, buffer_length, str_num.c_str(), *out_buffer_length );
return SQL_SUCCESS;
}
template <typename Number, typename Char>
SQLRETURN string_to_number( Char* string_data, SQLLEN str_len, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length, sqlsrv_error_auto_ptr& last_error )
SQLRETURN string_to_number( Char* string_data, SQLLEN str_len, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length, sqlsrv_error_auto_ptr& last_error )
{
Number* number_data = reinterpret_cast<Number*>( buffer );
std::locale loc; // default locale should match system
@ -170,13 +188,13 @@ sqlsrv_error* odbc_get_diag_rec( sqlsrv_stmt* odbc, SQLSMALLINT record_number )
// convert the error into the encoding of the context
sqlsrv_malloc_auto_ptr<SQLCHAR> sql_state;
SQLINTEGER sql_state_len = 0;
SQLLEN sql_state_len = 0;
if (!convert_string_from_utf16( enc, wsql_state, sizeof(wsql_state), (char**)&sql_state, sql_state_len )) {
return NULL;
}
sqlsrv_malloc_auto_ptr<SQLCHAR> native_message;
SQLINTEGER native_message_len = 0;
SQLLEN native_message_len = 0;
if (!convert_string_from_utf16( enc, wnative_message, wnative_message_len, (char**)&native_message, native_message_len )) {
return NULL;
}
@ -214,7 +232,7 @@ SQLRETURN sqlsrv_odbc_result_set::fetch( SQLSMALLINT orientation, SQLLEN offset
}
SQLRETURN sqlsrv_odbc_result_set::get_data( SQLUSMALLINT field_index, SQLSMALLINT target_type,
__out SQLPOINTER buffer, SQLLEN buffer_length, __out SQLLEN* out_buffer_length,
_Out_ SQLPOINTER buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length,
bool handle_warning TSRMLS_DC )
{
SQLSRV_ASSERT( odbc != NULL, "Invalid statement handle" );
@ -222,8 +240,8 @@ SQLRETURN sqlsrv_odbc_result_set::get_data( SQLUSMALLINT field_index, SQLSMALLIN
}
SQLRETURN sqlsrv_odbc_result_set::get_diag_field( SQLSMALLINT record_number, SQLSMALLINT diag_identifier,
__out SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
__out SQLSMALLINT* out_buffer_length TSRMLS_DC )
_Out_ SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
_Out_ SQLSMALLINT* out_buffer_length TSRMLS_DC )
{
SQLSRV_ASSERT( odbc != NULL, "Invalid statement handle" );
return core::SQLGetDiagField( odbc, record_number, diag_identifier, diag_info_buffer, buffer_length,
@ -257,9 +275,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( sqlsrv_stmt* stmt TSRMLS
{
// 10 is an arbitrary number for now for the initial size of the cache
ALLOC_HASHTABLE( cache );
core::sqlsrv_zend_hash_init( *stmt, cache, 10 /* # of buckets */, NULL /* hashfn */, cache_row_dtor /*dtor*/, 0 /*persistent*/
TSRMLS_CC );
core::sqlsrv_zend_hash_init( *stmt, cache, 10 /* # of buckets */, cache_row_dtor /*dtor*/, 0 /*persistent*/ TSRMLS_CC );
col_count = core::SQLNumResultCols( stmt TSRMLS_CC );
// there is no result set to buffer
if( col_count == 0 ) {
@ -441,7 +457,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( sqlsrv_stmt* stmt TSRMLS
// read the data into the cache
// (offset from the above loop has the size of the row buffer necessary)
unsigned long mem_used = 0;
zend_long mem_used = 0;
unsigned long row_count = 0;
while( core::SQLFetchScroll( stmt, SQL_FETCH_NEXT, 0 TSRMLS_CC ) != SQL_NO_DATA ) {
@ -478,7 +494,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( sqlsrv_stmt* stmt TSRMLS
}
else {
mem_used += meta[i].length;
mem_used += meta[i].length;
CHECK_CUSTOM_ERROR( mem_used > stmt->buffered_query_limit * 1024, stmt,
SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED, stmt->buffered_query_limit ) {
@ -523,7 +539,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( sqlsrv_stmt* stmt TSRMLS
// add it to the cache
row_dtor_closure cl( this, row );
sqlsrv_zend_hash_next_index_insert( *stmt, cache, &cl, sizeof( cl ) TSRMLS_CC );
sqlsrv_zend_hash_next_index_insert_mem( *stmt, cache, &cl, sizeof(row_dtor_closure) TSRMLS_CC );
}
}
@ -598,7 +614,7 @@ SQLRETURN sqlsrv_buffered_result_set::fetch( SQLSMALLINT orientation, SQLLEN off
}
SQLRETURN sqlsrv_buffered_result_set::get_data( SQLUSMALLINT field_index, SQLSMALLINT target_type,
__out SQLPOINTER buffer, SQLLEN buffer_length, __out SQLLEN* out_buffer_length,
_Out_ SQLPOINTER buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length,
bool handle_warning TSRMLS_DC )
{
last_error = NULL;
@ -634,8 +650,8 @@ SQLRETURN sqlsrv_buffered_result_set::get_data( SQLUSMALLINT field_index, SQLSMA
}
SQLRETURN sqlsrv_buffered_result_set::get_diag_field( SQLSMALLINT record_number, SQLSMALLINT diag_identifier,
__out SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
__out SQLSMALLINT* out_buffer_length TSRMLS_DC )
_Out_ SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
_Out_ SQLSMALLINT* out_buffer_length TSRMLS_DC )
{
SQLSRV_ASSERT( record_number == 1, "Only record number 1 can be fetched by sqlsrv_buffered_result_set::get_diag_field" );
SQLSRV_ASSERT( diag_identifier == SQL_DIAG_SQLSTATE,
@ -650,7 +666,7 @@ SQLRETURN sqlsrv_buffered_result_set::get_diag_field( SQLSMALLINT record_number,
SQLSRV_ASSERT( last_error->sqlstate != NULL,
"Must have a SQLSTATE in a valid last_error in sqlsrv_buffered_result_set::get_diag_field" );
memcpy( diag_info_buffer, last_error->sqlstate, min( buffer_length, SQL_SQLSTATE_BUFSIZE ));
memcpy_s( diag_info_buffer, buffer_length, last_error->sqlstate, min( buffer_length, SQL_SQLSTATE_BUFSIZE ));
return SQL_SUCCESS;
}
@ -658,8 +674,8 @@ SQLRETURN sqlsrv_buffered_result_set::get_diag_field( SQLSMALLINT record_number,
unsigned char* sqlsrv_buffered_result_set::get_row( void )
{
row_dtor_closure* cl_ptr;
int zr = zend_hash_index_find( cache, current - 1, (void**) &cl_ptr );
SQLSRV_ASSERT( zr == SUCCESS, "Failed to find row %1!d! in the cache", current );
cl_ptr = reinterpret_cast<row_dtor_closure*>(zend_hash_index_find_ptr(cache, static_cast<zend_ulong>(current - 1)));
SQLSRV_ASSERT(cl_ptr != NULL, "Failed to find row %1!d! in the cache", current);
return cl_ptr->row_data;
}
@ -686,8 +702,8 @@ SQLLEN sqlsrv_buffered_result_set::row_count( TSRMLS_D )
// private functions
template <typename Char>
SQLRETURN binary_to_string( SQLCHAR* field_data, SQLLEN& read_so_far, __out void* buffer,
SQLLEN buffer_length, __out SQLLEN* out_buffer_length,
SQLRETURN binary_to_string( SQLCHAR* field_data, SQLLEN& read_so_far, _Out_ void* buffer,
SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length,
sqlsrv_error_auto_ptr& out_error )
{
// hex characters for the conversion loop below
@ -747,8 +763,8 @@ SQLRETURN binary_to_string( SQLCHAR* field_data, SQLLEN& read_so_far, __out voi
return r;
}
SQLRETURN sqlsrv_buffered_result_set::binary_to_system_string( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::binary_to_system_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLCHAR* row = get_row();
SQLCHAR* field_data = NULL;
@ -765,8 +781,8 @@ SQLRETURN sqlsrv_buffered_result_set::binary_to_system_string( SQLSMALLINT field
return binary_to_string<char>( field_data, read_so_far, buffer, buffer_length, out_buffer_length, last_error );
}
SQLRETURN sqlsrv_buffered_result_set::binary_to_wide_string( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::binary_to_wide_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLCHAR* row = get_row();
SQLCHAR* field_data = NULL;
@ -784,11 +800,11 @@ SQLRETURN sqlsrv_buffered_result_set::binary_to_wide_string( SQLSMALLINT field_i
}
SQLRETURN sqlsrv_buffered_result_set::double_to_long( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::double_to_long( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_DOUBLE, "Invalid conversion to long" );
SQLSRV_ASSERT( buffer_length >= sizeof(LONG), "Buffer length must be able to find a long in "
SQLSRV_ASSERT( buffer_length >= sizeof(SQLLEN), "Buffer length must be able to find a long in "
"sqlsrv_buffered_result_set::double_to_long" );
unsigned char* row = get_row();
@ -813,8 +829,8 @@ SQLRETURN sqlsrv_buffered_result_set::double_to_long( SQLSMALLINT field_index, _
return SQL_SUCCESS;
}
SQLRETURN sqlsrv_buffered_result_set::double_to_system_string( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::double_to_system_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_DOUBLE, "Invalid conversion to system string" );
SQLSRV_ASSERT( buffer_length > 0, "Buffer length must be > 0 in sqlsrv_buffered_result_set::double_to_system_string" );
@ -825,8 +841,8 @@ SQLRETURN sqlsrv_buffered_result_set::double_to_system_string( SQLSMALLINT field
return number_to_string<char>( double_data, buffer, buffer_length, out_buffer_length, last_error );
}
SQLRETURN sqlsrv_buffered_result_set::double_to_wide_string( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::double_to_wide_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_DOUBLE, "Invalid conversion to wide string" );
SQLSRV_ASSERT( buffer_length > 0, "Buffer length must be > 0 in sqlsrv_buffered_result_set::double_to_wide_string" );
@ -837,8 +853,8 @@ SQLRETURN sqlsrv_buffered_result_set::double_to_wide_string( SQLSMALLINT field_i
return number_to_string<WCHAR>( double_data, buffer, buffer_length, out_buffer_length, last_error );
}
SQLRETURN sqlsrv_buffered_result_set::long_to_double( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::long_to_double( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_LONG, "Invalid conversion to long" );
SQLSRV_ASSERT( buffer_length >= sizeof(double), "Buffer length must be able to find a long in sqlsrv_buffered_result_set::double_to_long" );
@ -852,8 +868,8 @@ SQLRETURN sqlsrv_buffered_result_set::long_to_double( SQLSMALLINT field_index, _
return SQL_SUCCESS;
}
SQLRETURN sqlsrv_buffered_result_set::long_to_system_string( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::long_to_system_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_LONG, "Invalid conversion to system string" );
SQLSRV_ASSERT( buffer_length > 0, "Buffer length must be > 0 in sqlsrv_buffered_result_set::long_to_system_string" );
@ -864,8 +880,8 @@ SQLRETURN sqlsrv_buffered_result_set::long_to_system_string( SQLSMALLINT field_i
return number_to_string<char>( long_data, buffer, buffer_length, out_buffer_length, last_error );
}
SQLRETURN sqlsrv_buffered_result_set::long_to_wide_string( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::long_to_wide_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_LONG, "Invalid conversion to wide string" );
SQLSRV_ASSERT( buffer_length > 0, "Buffer length must be > 0 in sqlsrv_buffered_result_set::long_to_wide_string" );
@ -876,8 +892,8 @@ SQLRETURN sqlsrv_buffered_result_set::long_to_wide_string( SQLSMALLINT field_ind
return number_to_string<WCHAR>( long_data, buffer, buffer_length, out_buffer_length, last_error );
}
SQLRETURN sqlsrv_buffered_result_set::string_to_double( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::string_to_double( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_CHAR, "Invalid conversion from string to double" );
SQLSRV_ASSERT( buffer_length >= sizeof( double ), "Buffer needs to be big enough to hold a double" );
@ -888,8 +904,8 @@ SQLRETURN sqlsrv_buffered_result_set::string_to_double( SQLSMALLINT field_index,
return string_to_number<double>( string_data, meta[ field_index ].length, buffer, buffer_length, out_buffer_length, last_error );
}
SQLRETURN sqlsrv_buffered_result_set::wstring_to_double( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::wstring_to_double( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_WCHAR, "Invalid conversion from wide string to double" );
SQLSRV_ASSERT( buffer_length >= sizeof( double ), "Buffer needs to be big enough to hold a double" );
@ -900,8 +916,8 @@ SQLRETURN sqlsrv_buffered_result_set::wstring_to_double( SQLSMALLINT field_index
return string_to_number<double>( string_data, meta[ field_index ].length, buffer, buffer_length, out_buffer_length, last_error );
}
SQLRETURN sqlsrv_buffered_result_set::string_to_long( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::string_to_long( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_CHAR, "Invalid conversion from string to long" );
SQLSRV_ASSERT( buffer_length >= sizeof( LONG ), "Buffer needs to be big enough to hold a long" );
@ -912,8 +928,8 @@ SQLRETURN sqlsrv_buffered_result_set::string_to_long( SQLSMALLINT field_index, _
return string_to_number<LONG>( string_data, meta[ field_index ].length, buffer, buffer_length, out_buffer_length, last_error );
}
SQLRETURN sqlsrv_buffered_result_set::wstring_to_long( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::wstring_to_long( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_WCHAR, "Invalid conversion from wide string to long" );
SQLSRV_ASSERT( buffer_length >= sizeof( LONG ), "Buffer needs to be big enough to hold a long" );
@ -924,8 +940,8 @@ SQLRETURN sqlsrv_buffered_result_set::wstring_to_long( SQLSMALLINT field_index,
return string_to_number<LONG>( string_data, meta[ field_index ].length, buffer, buffer_length, out_buffer_length, last_error );
}
SQLRETURN sqlsrv_buffered_result_set::system_to_wide_string( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::system_to_wide_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( last_error == NULL, "Pending error for sqlsrv_buffered_results_set::system_to_wide_string" );
SQLSRV_ASSERT( buffer_length % 2 == 0, "Odd buffer length passed to sqlsrv_buffered_result_set::system_to_wide_string" );
@ -974,8 +990,13 @@ SQLRETURN sqlsrv_buffered_result_set::system_to_wide_string( SQLSMALLINT field_i
bool tried_again = false;
do {
int ch_space = MultiByteToWideChar( CP_ACP, MB_ERR_INVALID_CHARS, (LPCSTR) field_data, to_copy,
(LPWSTR) buffer, to_copy );
if (to_copy > INT_MAX ) {
LOG(SEV_ERROR, "MultiByteToWideChar: Buffer length exceeded.");
throw core::CoreException();
}
int ch_space = MultiByteToWideChar( CP_ACP, MB_ERR_INVALID_CHARS, (LPCSTR) field_data, static_cast<int>(to_copy),
static_cast<LPWSTR>(buffer), static_cast<int>(to_copy));
if( ch_space == 0 ) {
switch( GetLastError() ) {
@ -1012,8 +1033,8 @@ SQLRETURN sqlsrv_buffered_result_set::system_to_wide_string( SQLSMALLINT field_i
return r;
}
SQLRETURN sqlsrv_buffered_result_set::to_same_string( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::to_same_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( last_error == NULL, "Pending error for sqlsrv_buffered_results_set::to_same_string" );
@ -1071,19 +1092,19 @@ SQLRETURN sqlsrv_buffered_result_set::to_same_string( SQLSMALLINT field_index, _
SQLSRV_ASSERT( to_copy >= 0, "Negative field length calculated in buffered result set" );
if( to_copy > 0 ) {
memcpy( buffer, field_data + read_so_far, to_copy );
memcpy_s( buffer, buffer_length, field_data + read_so_far, to_copy );
read_so_far += to_copy;
}
if( extra ) {
OACR_WARNING_SUPPRESS( 26001, "Buffer length verified above" );
memcpy( reinterpret_cast<SQLCHAR*>( buffer ) + to_copy, L"\0", extra );
memcpy_s( reinterpret_cast<SQLCHAR*>( buffer ) + to_copy, buffer_length, L"\0", extra );
}
return r;
}
SQLRETURN sqlsrv_buffered_result_set::wide_to_system_string( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::wide_to_system_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( last_error == NULL, "Pending error for sqlsrv_buffered_results_set::wide_to_system_string" );
@ -1091,7 +1112,7 @@ SQLRETURN sqlsrv_buffered_result_set::wide_to_system_string( SQLSMALLINT field_i
unsigned char* row = get_row();
SQLCHAR* field_data = NULL;
SQLULEN field_len = NULL;
SQLLEN field_len = NULL;
// if this is the first time called for this field, just convert the entire string to system first then
// use that to read from instead of converting chunk by chunk. This is because it's impossible to know
@ -1116,9 +1137,9 @@ SQLRETURN sqlsrv_buffered_result_set::wide_to_system_string( SQLSMALLINT field_i
char default_char = '?';
// allocate enough to handle WC -> DBCS conversion if it happens
temp_string = reinterpret_cast<SQLCHAR*>( sqlsrv_malloc( field_len, sizeof( char ), sizeof(char)));
temp_length = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR) field_data, field_len / sizeof(WCHAR),
(LPSTR) temp_string.get(), field_len, &default_char, &default_char_used );
temp_string = reinterpret_cast<SQLCHAR*>( sqlsrv_malloc( field_len, sizeof(char), sizeof(char)));
temp_length = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR) field_data, static_cast<int>(field_len / sizeof(WCHAR)),
(LPSTR) temp_string.get(), static_cast<int>(field_len), &default_char, &default_char_used );
if( temp_length == 0 ) {
@ -1156,7 +1177,7 @@ SQLRETURN sqlsrv_buffered_result_set::wide_to_system_string( SQLSMALLINT field_i
if( to_copy > 0 ) {
memcpy( buffer, temp_string.get() + read_so_far, to_copy );
memcpy_s( buffer, buffer_length, temp_string.get() + read_so_far, to_copy );
}
SQLSRV_ASSERT( to_copy >= 0, "Invalid field copy length" );
OACR_WARNING_SUPPRESS( BUFFER_UNDERFLOW, "Buffer length verified above" );
@ -1167,14 +1188,14 @@ SQLRETURN sqlsrv_buffered_result_set::wide_to_system_string( SQLSMALLINT field_i
}
SQLRETURN sqlsrv_buffered_result_set::to_binary_string( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::to_binary_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
return to_same_string( field_index, buffer, buffer_length, out_buffer_length );
}
SQLRETURN sqlsrv_buffered_result_set::to_long( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::to_long( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_LONG, "Invlid conversion to long" );
SQLSRV_ASSERT( buffer_length >= sizeof( LONG ), "Buffer too small for SQL_C_LONG" ); // technically should ignore this
@ -1182,14 +1203,14 @@ SQLRETURN sqlsrv_buffered_result_set::to_long( SQLSMALLINT field_index, __out vo
unsigned char* row = get_row();
LONG* long_data = reinterpret_cast<LONG*>( &row[ meta[ field_index ].offset ] );
memcpy( buffer, long_data, sizeof( LONG ));
memcpy_s( buffer, buffer_length, long_data, sizeof( LONG ));
*out_buffer_length = sizeof( LONG );
return SQL_SUCCESS;
}
SQLRETURN sqlsrv_buffered_result_set::to_double( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::to_double( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_DOUBLE, "Invlid conversion to double" );
SQLSRV_ASSERT( buffer_length >= sizeof( double ), "Buffer too small for SQL_C_DOUBLE" ); // technically should ignore this
@ -1197,7 +1218,7 @@ SQLRETURN sqlsrv_buffered_result_set::to_double( SQLSMALLINT field_index, __out
unsigned char* row = get_row();
double* double_data = reinterpret_cast<double*>( &row[ meta[ field_index ].offset ] );
memcpy( buffer, double_data, sizeof( double ));
memcpy_s( buffer, buffer_length, double_data, sizeof( double ));
*out_buffer_length = sizeof( double );
return SQL_SUCCESS;
@ -1206,9 +1227,9 @@ SQLRETURN sqlsrv_buffered_result_set::to_double( SQLSMALLINT field_index, __out
namespace {
// called for each row in the cache when the cache is destroyed in the destructor
void cache_row_dtor( void* data )
void cache_row_dtor( zval* data )
{
row_dtor_closure* cl = reinterpret_cast<row_dtor_closure*>( data );
row_dtor_closure* cl = reinterpret_cast<row_dtor_closure*>( Z_PTR_P( data ) );
BYTE* row = cl->row_data;
// don't release this here, since this is called from the destructor of the result_set
sqlsrv_buffered_result_set* result_set = cl->results;
@ -1223,13 +1244,14 @@ void cache_row_dtor( void* data )
}
sqlsrv_free( row );
sqlsrv_free( cl );
}
SQLPOINTER read_lob_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_buffered_result_set::meta_data& meta,
unsigned long mem_used TSRMLS_DC )
zend_long mem_used TSRMLS_DC )
{
SQLSMALLINT extra = 0;
SQLLEN* output_buffer_len = NULL;
SQLULEN* output_buffer_len = NULL;
// Set the amount of space necessary for null characters at the end of the data.
switch( meta.c_type ) {
@ -1259,7 +1281,7 @@ SQLPOINTER read_lob_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_b
do {
output_buffer_len = reinterpret_cast<SQLLEN*>( buffer.get() );
output_buffer_len = reinterpret_cast<SQLULEN*>( buffer.get() );
r = core::SQLGetData( stmt, field_index + 1, meta.c_type, buffer.get() + already_read + sizeof( SQLULEN ),
to_read - already_read + extra, &last_field_len, false /*handle_warning*/ TSRMLS_CC );
@ -1303,7 +1325,7 @@ SQLPOINTER read_lob_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_b
already_read += to_read - already_read;
to_read = last_field_len;
buffer.resize( to_read + extra + sizeof( SQLULEN ));
output_buffer_len = reinterpret_cast<SQLLEN*>( buffer.get() );
output_buffer_len = reinterpret_cast<SQLULEN*>( buffer.get() );
// record the size of the field since we have it available
*output_buffer_len = last_field_len;
full_length_returned = true;
@ -1318,7 +1340,7 @@ SQLPOINTER read_lob_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_b
throw core::CoreException();
}
buffer.resize( to_read + extra + sizeof( SQLULEN ));
output_buffer_len = reinterpret_cast<SQLLEN*>( buffer.get() );
output_buffer_len = reinterpret_cast<SQLULEN*>( buffer.get() );
}
} while( true );

View file

@ -6,7 +6,7 @@
//
// Contents: Core routines and constants shared by the Microsoft Drivers for PHP for SQL Server
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -107,7 +107,7 @@ OACR_WARNING_POP
#include <limits>
#include <cassert>
#include <strsafe.h>
#include <memory>
// included for SQL Server specific constants
#include "msodbcsql.h"
@ -182,7 +182,7 @@ union sqlsrv_sqltype {
int scale:8;
} typeinfo;
long value;
zend_long value;
};
@ -196,7 +196,7 @@ union sqlsrv_phptype {
unsigned encoding:16;
} typeinfo;
long value;
zend_long value;
};
// static assert for enforcing compile time conditions
@ -500,6 +500,16 @@ public:
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
@ -656,7 +666,7 @@ public:
void reset( zval* ptr = NULL )
{
if( _ptr )
zval_ptr_dtor( &_ptr );
zval_ptr_dtor(_ptr );
_ptr = ptr;
}
@ -664,12 +674,7 @@ public:
{
return sqlsrv_auto_ptr<zval, zval_auto_ptr>::operator=( ptr );
}
#if PHP_MAJOR_VERSION > 5 || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 3)
operator zval_gc_info*( void )
{
return reinterpret_cast<zval_gc_info*>(_ptr);
}
#endif
private:
@ -884,6 +889,8 @@ class sqlsrv_context {
{
if( handle_ != SQL_NULL_HANDLE ) {
::SQLFreeHandle( handle_type_, handle_ );
last_error_.reset();
}
handle_ = SQL_NULL_HANDLE;
}
@ -919,7 +926,7 @@ const int SQLSRV_OS_VISTA_OR_LATER = 6; // major version for Vista
struct sqlsrv_encoding {
const char* iana;
unsigned int iana_len;
size_t iana_len;
unsigned int code_page;
bool not_for_connection;
@ -965,6 +972,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;
@ -976,6 +991,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 )
@ -1010,7 +1027,7 @@ const char ConnectionPooling[] = "ConnectionPooling";
const char Database[] = "Database";
const char Encrypt[] = "Encrypt";
const char Failover_Partner[] = "Failover_Partner";
const char LoginTimeout[] = "LoginTimggeout";
const char LoginTimeout[] = "LoginTimeout";
const char MARS_ODBC[] = "MARS_Connection";
const char MultiSubnetFailover[] = "MultiSubnetFailover";
const char QuotedId[] = "QuotedId";
@ -1087,9 +1104,8 @@ 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 )),
Z_STRLEN_P( value ) TSRMLS_CC );
static_cast<SQLINTEGER>(Z_STRLEN_P( value )) TSRMLS_CC );
}
catch( core::CoreException& ) {
throw;
@ -1118,15 +1134,15 @@ sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_
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, long sql_len 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 );
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 );
bool core_is_conn_opt_value_escaped( const char* value, int value_len );
int core_str_zval_is_true( zval* str_zval );
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 );
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
@ -1158,7 +1174,7 @@ struct stmt_option {
const char * name; // name of the statement option
unsigned int name_len; // name length
unsigned int key;
stmt_option_functor* func; // callback that actually handles the work of the option
std::unique_ptr<stmt_option_functor> func; // callback that actually handles the work of the option
};
@ -1170,7 +1186,7 @@ struct sqlsrv_stream {
SQLUSMALLINT field_index;
SQLSMALLINT sql_type;
sqlsrv_stmt* stmt;
int stmt_index;
std::size_t stmt_index;
sqlsrv_stream( zval* str_z, SQLSRV_ENCODING enc ) :
stream_z( str_z ), encoding( enc )
@ -1183,7 +1199,7 @@ struct sqlsrv_stream {
};
// close any active stream
void close_active_stream( __inout sqlsrv_stmt* stmt TSRMLS_DC );
void close_active_stream( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC );
extern php_stream_wrapper g_sqlsrv_stream_wrapper;
@ -1197,7 +1213,7 @@ struct sqlsrv_output_param {
zval* param_z;
SQLSRV_ENCODING encoding;
int param_num; // used to index into the ind_or_len of the statement
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;
@ -1239,28 +1255,28 @@ struct sqlsrv_stmt : public sqlsrv_context {
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
unsigned long buffered_query_limit; // maximum allowed memory for a buffered query (measured in KB)
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
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
zval* active_stream; // the currently active stream reading data from the database
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
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
virtual sqlsrv_phptype sql_type_to_php_type( SQLINTEGER sql_type, SQLUINTEGER size, bool prefer_string_to_stream ) = 0;
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;
};
@ -1302,25 +1318,25 @@ typedef sqlsrv_stmt* (*driver_stmt_factory)( sqlsrv_conn* conn, SQLHANDLE h, err
// *** 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, unsigned int param_num, int direction, zval* param_z,
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, SQLLEN 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 );
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);
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, unsigned int paramno, zval* param_z TSRMLS_DC );
void core_sqlsrv_set_scrollable( sqlsrv_stmt* stmt, unsigned int cursor_type TSRMLS_DC );
void core_sqlsrv_post_param( sqlsrv_stmt* stmt, zend_ulong paramno, zval* param_z TSRMLS_DC );
void core_sqlsrv_set_scrollable( sqlsrv_stmt* stmt, unsigned long cursor_type TSRMLS_DC );
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, long limit TSRMLS_DC );
void core_sqlsrv_set_buffered_query_limit( sqlsrv_stmt* stmt, SQLLEN limit TSRMLS_DC );
//*********************************************************************************************************************************
@ -1344,11 +1360,11 @@ struct sqlsrv_result_set {
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,
__out void* buffer, SQLLEN buffer_length, __out SQLLEN* out_buffer_length,
_Out_ void* buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length,
bool handle_warning TSRMLS_DC )= 0;
virtual SQLRETURN get_diag_field( SQLSMALLINT record_number, SQLSMALLINT diag_identifier,
__out SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
__out SQLSMALLINT* out_buffer_length TSRMLS_DC ) = 0;
_Out_ SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
_Out_ SQLSMALLINT* out_buffer_length TSRMLS_DC ) = 0;
virtual sqlsrv_error* get_diag_rec( SQLSMALLINT record_number ) = 0;
virtual SQLLEN row_count( TSRMLS_D ) = 0;
};
@ -1356,16 +1372,16 @@ struct sqlsrv_result_set {
struct sqlsrv_odbc_result_set : public sqlsrv_result_set {
explicit sqlsrv_odbc_result_set( sqlsrv_stmt* );
virtual ~sqlsrv_odbc_result_set( void );
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,
__out void* buffer, SQLLEN buffer_length, __out SQLLEN* out_buffer_length,
_Out_ void* buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length,
bool handle_warning TSRMLS_DC );
virtual SQLRETURN get_diag_field( SQLSMALLINT record_number, SQLSMALLINT diag_identifier,
__out SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
__out SQLSMALLINT* out_buffer_length TSRMLS_DC );
_Out_ SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
_Out_ SQLSMALLINT* out_buffer_length TSRMLS_DC );
virtual sqlsrv_error* get_diag_rec( SQLSMALLINT record_number );
virtual SQLLEN row_count( TSRMLS_D );
@ -1390,8 +1406,8 @@ struct sqlsrv_buffered_result_set : public sqlsrv_result_set {
// 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 unsigned long BUFFERED_QUERY_LIMIT_DEFAULT = 10240; // measured in KB
static const long BUFFERED_QUERY_LIMIT_INVALID = 0;
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 );
@ -1399,11 +1415,11 @@ struct sqlsrv_buffered_result_set : public sqlsrv_result_set {
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,
__out void* buffer, SQLLEN buffer_length, __out SQLLEN* out_buffer_length,
_Out_ void* buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length,
bool handle_warning TSRMLS_DC );
virtual SQLRETURN get_diag_field( SQLSMALLINT record_number, SQLSMALLINT diag_identifier,
__out SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
__out SQLSMALLINT* out_buffer_length TSRMLS_DC );
_Out_ SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
_Out_ SQLSMALLINT* out_buffer_length TSRMLS_DC );
virtual sqlsrv_error* get_diag_rec( SQLSMALLINT record_number );
virtual SQLLEN row_count( TSRMLS_D );
@ -1434,55 +1450,55 @@ struct sqlsrv_buffered_result_set : public sqlsrv_result_set {
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
typedef SQLRETURN (sqlsrv_buffered_result_set::*conv_fn)( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length );
typedef SQLRETURN (sqlsrv_buffered_result_set::*conv_fn)( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length );
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
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 );
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 );
// long conversion functions
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 );
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 );
// double conversion functions
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 );
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 );
// string to number conversion functions
// Future: See if these can be converted directly to template member functions
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 );
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 );
// utility functions for conversions
unsigned char* get_row( void );
@ -1500,11 +1516,12 @@ struct sqlsrv_buffered_result_set : public sqlsrv_result_set {
#define MEMCHECK_SILENT 1
// utility functions shared by multiple callers across files
bool convert_string_from_utf16_inplace( SQLSRV_ENCODING encoding, char** string, SQLINTEGER& len);
bool convert_string_from_utf16( SQLSRV_ENCODING encoding, const wchar_t* inString, SQLINTEGER cchInLen, char** outString, SQLINTEGER& cchOutLen );
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,
unsigned int mbcs_len, __out unsigned int* utf16_len );
unsigned int mbcs_len, _Out_ unsigned int* utf16_len );
//*********************************************************************************************************************************
// Error handling routines and Predefined Errors
@ -1559,8 +1576,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[];
@ -1582,7 +1599,7 @@ enum error_handling_flags {
// 2/code) driver specific error code
// 3/message) driver specific error message
// The fetch type determines if the indices are numeric, associative, or both.
bool core_sqlsrv_get_odbc_error( sqlsrv_context& ctx, int record_number, __out sqlsrv_error_auto_ptr& error,
bool core_sqlsrv_get_odbc_error( sqlsrv_context& ctx, int record_number, _Out_ sqlsrv_error_auto_ptr& error,
logging_severity severity TSRMLS_DC );
// format and return a driver specfic error
@ -1595,11 +1612,11 @@ void core_sqlsrv_format_driver_error( sqlsrv_context& ctx, sqlsrv_error_const co
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
DWORD core_sqlsrv_format_message( char* output_buffer, unsigned output_len, const char* format, ... );
DWORD core_sqlsrv_format_message( char*& output_buffer, unsigned output_len, const char* format, ... );
// 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 int sqlsrv_error_code TSRMLS_DC, bool warning, ... )
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 );
@ -1608,7 +1625,7 @@ inline bool call_error_handler( sqlsrv_context& ctx, unsigned int sqlsrv_error_c
return ignored;
}
inline bool call_error_handler( sqlsrv_context* ctx, unsigned int sqlsrv_error_code TSRMLS_DC, bool warning, ... )
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 );
@ -1737,9 +1754,8 @@ 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( !strcmp( reinterpret_cast<const char*>( err_msg ), CONNECTION_BUSY_ODBC_ERROR[driver_version] )) {
THROW_CORE_ERROR( stmt, SQLSRV_ERROR_MARS_OFF );
}
@ -1755,8 +1771,8 @@ namespace core {
// the context to hold the error, they are not passed as const.
inline SQLRETURN SQLGetDiagField( sqlsrv_context* ctx, SQLSMALLINT record_number, SQLSMALLINT diag_identifier,
__out SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
__out SQLSMALLINT* out_buffer_length TSRMLS_DC )
_Out_ SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
_Out_ SQLSMALLINT* out_buffer_length TSRMLS_DC )
{
SQLRETURN r = ::SQLGetDiagField( ctx->handle_type(), ctx->handle(), record_number, diag_identifier,
diag_info_buffer, buffer_length, out_buffer_length );
@ -1769,7 +1785,7 @@ namespace core {
}
inline void SQLAllocHandle( SQLSMALLINT HandleType, sqlsrv_context& InputHandle,
__out_ecount(1) SQLHANDLE* OutputHandlePtr TSRMLS_DC )
_Out_writes_(1) SQLHANDLE* OutputHandlePtr TSRMLS_DC )
{
SQLRETURN r;
r = ::SQLAllocHandle( HandleType, InputHandle.handle(), OutputHandlePtr );
@ -1785,9 +1801,9 @@ namespace core {
SQLSMALLINT ParameterType,
SQLULEN ColumnSize,
SQLSMALLINT DecimalDigits,
__inout SQLPOINTER ParameterValuePtr,
_Inout_ SQLPOINTER ParameterValuePtr,
SQLLEN BufferLength,
__inout SQLLEN * StrLen_Or_IndPtr
_Inout_ SQLLEN * StrLen_Or_IndPtr
TSRMLS_DC )
{
SQLRETURN r;
@ -1801,8 +1817,8 @@ namespace core {
inline void SQLColAttribute( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, SQLUSMALLINT field_identifier,
__out SQLPOINTER field_type_char, SQLSMALLINT buffer_length,
__out SQLSMALLINT* out_buffer_length, __out SQLLEN* field_type_num TSRMLS_DC )
_Out_ SQLPOINTER field_type_char, SQLSMALLINT buffer_length,
_Out_ SQLSMALLINT* out_buffer_length, _Out_ SQLLEN* field_type_num TSRMLS_DC )
{
SQLRETURN r = ::SQLColAttribute( stmt->handle(), field_index, field_identifier, field_type_char,
buffer_length, out_buffer_length, field_type_num );
@ -1813,9 +1829,9 @@ namespace core {
}
inline void SQLDescribeCol( sqlsrv_stmt* stmt, SQLSMALLINT colno, __out_z 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 )
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 )
{
SQLRETURN r;
r = ::SQLDescribeCol( stmt->handle(), colno, col_name, col_name_length, col_name_length_out,
@ -1898,7 +1914,7 @@ namespace core {
}
inline SQLRETURN SQLGetData( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, SQLSMALLINT target_type,
__out void* buffer, SQLLEN buffer_length, __out SQLLEN* out_buffer_length,
_Out_ void* buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length,
bool handle_warning TSRMLS_DC )
{
SQLRETURN r = ::SQLGetData( stmt->handle(), field_index, target_type, buffer, buffer_length, out_buffer_length );
@ -1920,8 +1936,8 @@ namespace core {
}
inline void SQLGetInfo( sqlsrv_conn* conn, SQLUSMALLINT info_type, __out SQLPOINTER info_value, SQLSMALLINT buffer_len,
__out SQLSMALLINT* str_len TSRMLS_DC )
inline void SQLGetInfo( sqlsrv_conn* conn, SQLUSMALLINT info_type, _Out_ SQLPOINTER info_value, SQLSMALLINT buffer_len,
_Out_ SQLSMALLINT* str_len TSRMLS_DC )
{
SQLRETURN r;
r = ::SQLGetInfo( conn->handle(), info_type, info_value, buffer_len, str_len );
@ -1970,7 +1986,7 @@ namespace core {
// 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.
inline SQLRETURN SQLParamData( sqlsrv_stmt* stmt, __out SQLPOINTER* value_ptr_ptr TSRMLS_DC )
inline SQLRETURN SQLParamData( sqlsrv_stmt* stmt, _Out_ SQLPOINTER* value_ptr_ptr TSRMLS_DC )
{
SQLRETURN r;
r = ::SQLParamData( stmt->handle(), value_ptr_ptr );
@ -2055,6 +2071,25 @@ namespace core {
// *** zend wrappers ***
//zend_resource_dtor sets the type of destroyed resources to -1
#define RSRC_INVALID_TYPE -1
// 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
@ -2063,7 +2098,7 @@ namespace core {
// 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, unsigned int index, zval* value TSRMLS_DC)
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 ) {
@ -2079,7 +2114,7 @@ namespace core {
}
}
inline void sqlsrv_add_assoc_null( sqlsrv_context& ctx, zval* array_z, char* key TSRMLS_DC )
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 ) {
@ -2087,7 +2122,7 @@ namespace core {
}
}
inline void sqlsrv_add_assoc_long( sqlsrv_context& ctx, zval* array_z, char* key, long val TSRMLS_DC )
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 ) {
@ -2095,23 +2130,26 @@ namespace core {
}
}
inline void sqlsrv_add_assoc_string( sqlsrv_context& ctx, zval* array_z, char* key, char* val, bool duplicate TSRMLS_DC )
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, duplicate );
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);
}
}
inline void sqlsrv_array_init( sqlsrv_context& ctx, __out zval* new_array TSRMLS_DC)
inline void sqlsrv_array_init( sqlsrv_context& ctx, _Out_ zval* new_array TSRMLS_DC)
{
int zr = ::array_init( new_array );
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 )
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 );
@ -2120,53 +2158,89 @@ namespace core {
}
}
inline void sqlsrv_zend_hash_get_current_data( sqlsrv_context& ctx, HashTable* ht, __out void** output_data TSRMLS_DC )
inline void sqlsrv_zend_hash_get_current_data(sqlsrv_context& ctx, HashTable* ht, _Out_ zval*& output_data TSRMLS_DC)
{
int zr = (output_data = ::zend_hash_get_current_data(ht)) != NULL ? SUCCESS : FAILURE;
CHECK_ZEND_ERROR( zr, ctx, SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException();
}
}
inline void sqlsrv_zend_hash_get_current_data_ptr(sqlsrv_context& ctx, HashTable* ht, _Out_ void*& output_data TSRMLS_DC)
{
int zr = ::zend_hash_get_current_data( ht, output_data );
CHECK_ZEND_ERROR( zr, ctx, SQLSRV_ERROR_ZEND_HASH ) {
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, int index TSRMLS_DC )
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, unsigned long index, void* data,
uint data_size TSRMLS_DC )
inline void sqlsrv_zend_hash_index_update( sqlsrv_context& ctx, HashTable* ht, zend_ulong index, zval* data_z TSRMLS_DC )
{
int zr = ::zend_hash_index_update( ht, index, data, data_size, NULL );
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_next_index_insert( sqlsrv_context& ctx, HashTable* ht, void* data,
uint data_size TSRMLS_DC )
inline void sqlsrv_zend_hash_index_update_ptr(sqlsrv_context& ctx, HashTable* ht, zend_ulong index, void* pData TSRMLS_DC)
{
int zr = ::zend_hash_next_index_insert( ht, data, data_size, NULL );
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_init( sqlsrv_context& ctx, HashTable* ht, unsigned int initial_size, hash_func_t hash_fn,
dtor_func_t dtor_fn, zend_bool persistent TSRMLS_DC )
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 = ::zend_hash_init( ht, initial_size, hash_fn, dtor_fn, persistent );
CHECK_ZEND_ERROR( zr, ctx, SQLSRV_ERROR_ZEND_HASH ) {
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_add( sqlsrv_context& ctx, HashTable* ht, char* key, unsigned int key_len, void** data,
unsigned int data_size, void **pDest TSRMLS_DC )
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 )
{
int zr = ::zend_hash_add( ht, key, key_len, data, data_size, pDest );
::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();
}

View file

@ -3,7 +3,7 @@
//
// Contents: Core routines that use statement handles shared between sqlsrv and pdo_sqlsrv
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -24,7 +24,7 @@ namespace {
// certain drivers using this layer will call for repeated or out of order field retrievals. To allow this, we cache the
// results of every field request, and if it is out of order, we cache those for preceding fields.
struct field_cache {
void* value;
SQLLEN len;
sqlsrv_phptype type;
@ -35,7 +35,7 @@ struct field_cache {
// if the value is NULL, then just record a NULL pointer
if( field_value != NULL ) {
value = sqlsrv_malloc( field_len );
memcpy( value, field_value, field_len );
memcpy_s( value, field_len, field_value, field_len );
len = field_len;
}
else {
@ -76,36 +76,36 @@ const size_t DATE_FORMAT_LEN = sizeof( DATE_FORMAT );
// *** internal functions ***
// Only declarations are put here. Functions contain the documentation they need at their definition sites.
void calc_string_size( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, SQLLEN sql_type, __out SQLLEN& size TSRMLS_DC );
void calc_string_size( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, SQLLEN sql_type, _Out_ SQLLEN& size TSRMLS_DC );
size_t calc_utf8_missing( sqlsrv_stmt* stmt, const char* buffer, size_t buffer_end TSRMLS_DC );
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 );
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);
// returns the ODBC C type constant that matches the PHP type and encoding given
SQLSMALLINT default_c_type( sqlsrv_stmt* stmt, unsigned int paramno, zval const* param_z, SQLSRV_ENCODING encoding TSRMLS_DC );
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,
__out SQLULEN& column_size, __out SQLSMALLINT& decimal_digits TSRMLS_DC );
_Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits TSRMLS_DC );
// given a zval and encoding, determine the appropriate sql type, column size, and decimal scale (if appropriate)
void default_sql_type( sqlsrv_stmt* stmt, unsigned int paramno, zval* param_z, SQLSRV_ENCODING encoding,
__out SQLSMALLINT& sql_type TSRMLS_DC );
void field_cache_dtor( void* data );
void default_sql_type( sqlsrv_stmt* stmt, SQLULEN paramno, zval* param_z, SQLSRV_ENCODING encoding,
_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 );
stmt_option const* get_stmt_option( sqlsrv_conn const* conn, unsigned long key, const stmt_option stmt_opts[] 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
void resize_output_buffer_if_necessary( sqlsrv_stmt* stmt, zval* param_z, unsigned int paramno, SQLSRV_ENCODING encoding,
void resize_output_buffer_if_necessary( sqlsrv_stmt* stmt, zval* param_z, SQLULEN paramno, SQLSRV_ENCODING encoding,
SQLSMALLINT c_type, SQLSMALLINT sql_type, SQLULEN column_size, SQLPOINTER& buffer,
SQLLEN& buffer_len TSRMLS_DC );
void save_output_param_for_later( sqlsrv_stmt* stmt, sqlsrv_output_param& param TSRMLS_DC );
// send all the stream data
void send_param_streams( sqlsrv_stmt* stmt TSRMLS_DC );
// called when a bound output string parameter is to be destroyed
void sqlsrv_output_param_dtor( void* data );
void sqlsrv_output_param_dtor( zval* data );
// called when a bound stream parameter is to be destroyed.
void sqlsrv_stream_dtor( void* data );
void sqlsrv_stream_dtor( zval* data );
bool is_streamable_type( SQLINTEGER sql_type );
}
@ -127,43 +127,32 @@ 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)
MAKE_STD_ZVAL( param_input_strings );
core::sqlsrv_array_init( *conn, param_input_strings TSRMLS_CC );
core::sqlsrv_array_init( *conn, &param_input_strings TSRMLS_CC );
// initialize the (input only) stream parameters (which holds sqlsrv_stream structures)
MAKE_STD_ZVAL( param_streams );
Z_TYPE_P( param_streams ) = IS_ARRAY;
ALLOC_HASHTABLE( Z_ARRVAL_P( param_streams ));
core::sqlsrv_zend_hash_init( *conn, Z_ARRVAL_P( param_streams ), 5 /* # of buckets */, NULL /*hashfn*/,
sqlsrv_stream_dtor, 0 /*persistent*/ TSRMLS_CC );
ZVAL_NEW_ARR( &param_streams );
core::sqlsrv_zend_hash_init(*conn, Z_ARRVAL( param_streams ), 5 /* # of buckets */, sqlsrv_stream_dtor, 0 /*persistent*/ TSRMLS_CC);
// initialize the (input only) datetime parameters of converted date time objects to strings
MAKE_STD_ZVAL( param_datetime_buffers );
array_init( param_datetime_buffers );
array_init( &param_datetime_buffers );
// initialize the output string parameters (which holds sqlsrv_output_param structures)
MAKE_STD_ZVAL( output_params );
Z_TYPE_P( output_params ) = IS_ARRAY;
ALLOC_HASHTABLE( Z_ARRVAL_P( output_params ));
core::sqlsrv_zend_hash_init( *conn, Z_ARRVAL_P( output_params ), 5 /* # of buckets */, NULL /*hashfn*/,
sqlsrv_output_param_dtor, 0 /*persistent*/ TSRMLS_CC );
ZVAL_NEW_ARR( &output_params );
core::sqlsrv_zend_hash_init(*conn, Z_ARRVAL( output_params ), 5 /* # of buckets */, sqlsrv_output_param_dtor, 0 /*persistent*/ TSRMLS_CC);
// initialize the field cache
MAKE_STD_ZVAL( field_cache );
Z_TYPE_P( field_cache ) = IS_ARRAY;
ALLOC_HASHTABLE( Z_ARRVAL_P( field_cache ));
core::sqlsrv_zend_hash_init( *conn, Z_ARRVAL_P( field_cache ), 5 /* # of buckets */, NULL /*hashfn*/,
field_cache_dtor, 0 /*persistent*/ TSRMLS_CC );
ZVAL_NEW_ARR( &field_cache );
core::sqlsrv_zend_hash_init(*conn, Z_ARRVAL(field_cache), 5 /* # of buckets */, field_cache_dtor, 0 /*persistent*/ TSRMLS_CC);
}
// 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 );
}
@ -174,8 +163,8 @@ sqlsrv_stmt::~sqlsrv_stmt( void )
efree( current_results );
current_results = NULL;
}
invalidate();
invalidate();
zval_ptr_dtor( &param_input_strings );
zval_ptr_dtor( &output_params );
zval_ptr_dtor( &param_streams );
@ -189,13 +178,13 @@ sqlsrv_stmt::~sqlsrv_stmt( void )
// execution phase.
void sqlsrv_stmt::free_param_data( TSRMLS_D )
{
SQLSRV_ASSERT( Z_TYPE_P( param_input_strings ) == IS_ARRAY && Z_TYPE_P( param_streams ) == IS_ARRAY,
SQLSRV_ASSERT(Z_TYPE( param_input_strings ) == IS_ARRAY && Z_TYPE( param_streams ) == IS_ARRAY,
"sqlsrv_stmt::free_param_data: Param zvals aren't arrays." );
zend_hash_clean( Z_ARRVAL_P( param_input_strings ));
zend_hash_clean( Z_ARRVAL_P( output_params ));
zend_hash_clean( Z_ARRVAL_P( param_streams ));
zend_hash_clean( Z_ARRVAL_P( param_datetime_buffers ));
zend_hash_clean( Z_ARRVAL_P( field_cache ));
zend_hash_clean( Z_ARRVAL( param_input_strings ));
zend_hash_clean( Z_ARRVAL( output_params ));
zend_hash_clean( Z_ARRVAL( param_streams ));
zend_hash_clean( Z_ARRVAL( param_datetime_buffers ));
zend_hash_clean( Z_ARRVAL( field_cache ));
}
@ -242,7 +231,7 @@ void sqlsrv_stmt::new_result_set( TSRMLS_D )
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 )
{
sqlsrv_malloc_auto_ptr<sqlsrv_stmt> stmt;
sqlsrv_malloc_auto_ptr<sqlsrv_stmt> stmt;
SQLHANDLE stmt_h = SQL_NULL_HANDLE;
try {
@ -259,34 +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 ) {
zend_ulong index = -1;
zend_string *key = NULL;
zval* value_z = NULL;
for( zend_hash_internal_pointer_reset( options_ht );
zend_hash_has_more_elements( options_ht ) == SUCCESS;
zend_hash_move_forward( options_ht )) {
ZEND_HASH_FOREACH_KEY_VAL( options_ht, index, key, value_z ) {
char *key = NULL;
unsigned int key_len = 0;
unsigned long index = -1;
zval** value_z = NULL;
int type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG;
int type = zend_hash_get_current_key_ex( options_ht, &key, &key_len, &index, 0, NULL );
// 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, (void**) &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 );
}
// The driver layer should ensure a valid key.
DEBUG_SQLSRV_ASSERT(( type == HASH_KEY_IS_LONG ), "allocate_stmt: Invalid statment option key provided." );
zend_hash_internal_pointer_end( options_ht );
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;
@ -329,9 +310,9 @@ sqlsrv_stmt* core_sqlsrv_create_stmt( sqlsrv_conn* conn, driver_stmt_factory stm
// Return:
// Nothing, though an exception is thrown if an error occurs
// The php type of the parameter is taken from the zval.
// The sql type is given as a hint if the driver provides it.
// The sql type is given as a hint if the driver provides it.
void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int direction, zval* param_z,
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 )
{
@ -352,13 +333,17 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int dire
}
// resize the statements array of int_ptrs if the parameter isn't already set.
if( stmt->param_ind_ptrs.size() < param_num + 1 ) {
if( stmt->param_ind_ptrs.size() < static_cast<size_t>(param_num + 1) ) {
stmt->param_ind_ptrs.resize( param_num + 1, SQL_NULL_DATA );
}
SQLLEN& ind_ptr = stmt->param_ind_ptrs[ param_num ];
bool zval_was_null = (Z_TYPE_P( param_z ) == IS_NULL);
bool zval_was_bool = (Z_TYPE_P( param_z ) == IS_BOOL);
zval* param_ref = param_z;
if ( Z_ISREF_P( param_z ) ) {
ZVAL_DEREF( param_z );
}
bool zval_was_null = ( Z_TYPE_P( param_z ) == IS_NULL );
bool zval_was_bool = ( Z_TYPE_P( param_z ) == IS_TRUE || Z_TYPE_P( param_z ) == IS_FALSE );
// if the user asks for for a specific type for input and output, make sure the data type we send matches the data we
// type we expect back, since we can only send and receive the same type. Anything can be converted to a string, so
// we always let that match if they want a string back.
@ -426,12 +411,12 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int dire
// if the sql type is unknown, then set the default based on the PHP type passed in
if( sql_type == SQL_UNKNOWN_TYPE ) {
default_sql_type( stmt, param_num, param_z, encoding, sql_type TSRMLS_CC );
default_sql_type( stmt, param_num, param_z, encoding, sql_type TSRMLS_CC );
}
// if the size is unknown, then set the default based on the PHP type passed in
if( column_size == SQLSRV_UNKNOWN_SIZE ) {
default_sql_size_and_scale( stmt, param_num, param_z, encoding, column_size, decimal_digits TSRMLS_CC );
default_sql_size_and_scale( stmt, static_cast<unsigned int>( param_num ), param_z, encoding, column_size, decimal_digits TSRMLS_CC );
}
// determine the ODBC C type
@ -448,15 +433,18 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int dire
buffer_len = 0;
}
break;
case IS_BOOL:
case IS_TRUE:
case IS_FALSE:
case IS_LONG:
{
{
// if it is boolean, set the lval to 0 or 1
convert_to_long( param_z );
buffer = &param_z->value;
buffer_len = sizeof( param_z->value.lval );
buffer_len = sizeof( Z_LVAL_P( param_z ));
ind_ptr = buffer_len;
if( direction != SQL_PARAM_INPUT ) {
// save the parameter so that 1) the buffer doesn't go away, and 2) we can set it to NULL if returned
sqlsrv_output_param output_param( param_z, param_num, zval_was_bool );
sqlsrv_output_param output_param( param_ref, static_cast<int>( param_num ), zval_was_bool );
save_output_param_for_later( stmt, output_param TSRMLS_CC );
}
}
@ -464,11 +452,11 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int dire
case IS_DOUBLE:
{
buffer = &param_z->value;
buffer_len = sizeof( param_z->value.dval );
buffer_len = sizeof( Z_DVAL_P( param_z ));
ind_ptr = buffer_len;
if( direction != SQL_PARAM_INPUT ) {
// save the parameter so that 1) the buffer doesn't go away, and 2) we can set it to NULL if returned
sqlsrv_output_param output_param( param_z, param_num, false );
sqlsrv_output_param output_param( param_ref, static_cast<int>( param_num ), false );
save_output_param_for_later( stmt, output_param TSRMLS_CC );
}
}
@ -479,48 +467,43 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int dire
// if the encoding is UTF-8, translate from UTF-8 to UTF-16 (the type variables should have already been adjusted)
if( direction == SQL_PARAM_INPUT && encoding == CP_UTF8 ) {
zval_auto_ptr wbuffer_z;
ALLOC_INIT_ZVAL( wbuffer_z );
zval wbuffer_z;
ZVAL_NULL( &wbuffer_z );
bool converted = convert_input_param_to_utf16( param_z, wbuffer_z );
bool converted = convert_input_param_to_utf16( param_z, &wbuffer_z );
CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_INPUT_PARAM_ENCODING_TRANSLATE,
param_num + 1, get_last_error_message() ) {
throw core::CoreException();
}
buffer = Z_STRVAL_P( wbuffer_z );
buffer_len = Z_STRLEN_P( wbuffer_z );
core::sqlsrv_add_index_zval( *stmt, stmt->param_input_strings, param_num, wbuffer_z TSRMLS_CC );
wbuffer_z.transferred();
buffer = Z_STRVAL_P( &wbuffer_z );
buffer_len = Z_STRLEN_P( &wbuffer_z );
core::sqlsrv_add_index_zval(*stmt, &(stmt->param_input_strings), param_num, &wbuffer_z TSRMLS_CC);
}
ind_ptr = buffer_len;
if( direction != SQL_PARAM_INPUT ) {
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4
// PHP 5.4 added interned strings, so since we obviously want to change that string here in some fashion,
// we reallocate the string if it's interned
if( IS_INTERNED( buffer )) {
ZVAL_STRINGL( param_z, static_cast<const char*>(buffer), buffer_len, 1 );
buffer = Z_STRVAL_P( param_z );
buffer_len = Z_STRLEN_P( param_z );
}
#endif
// PHP 5.4 added interned strings, so since we obviously want to change that string here in some fashion,
// we reallocate the string if it's interned
if ( ZSTR_IS_INTERNED( Z_STR_P( param_z ))) {
core::sqlsrv_zval_stringl( param_z, static_cast<const char*>(buffer), buffer_len );
buffer = Z_STRVAL_P( param_z );
buffer_len = Z_STRLEN_P( param_z );
}
// if it's a UTF-8 input output parameter (signified by the C type being SQL_C_WCHAR)
// or if the PHP type is a binary encoded string with a N(VAR)CHAR/NTEXTSQL type,
// convert it to wchar first
if( direction == SQL_PARAM_INPUT_OUTPUT &&
(c_type == SQL_C_WCHAR ||
(c_type == SQL_C_BINARY &&
(sql_type == SQL_WCHAR ||
sql_type == SQL_WVARCHAR ||
sql_type == SQL_WLONGVARCHAR )))) {
( c_type == SQL_C_WCHAR ||
( c_type == SQL_C_BINARY &&
( sql_type == SQL_WCHAR ||
sql_type == SQL_WVARCHAR ||
sql_type == SQL_WLONGVARCHAR )))) {
bool converted = convert_input_param_to_utf16( param_z, param_z );
CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_INPUT_PARAM_ENCODING_TRANSLATE,
param_num + 1, get_last_error_message() ) {
throw core::CoreException();
}
sqlsrv_free( buffer );
buffer = Z_STRVAL_P( param_z );
buffer_len = Z_STRLEN_P( param_z );
ind_ptr = buffer_len;
@ -532,11 +515,12 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int dire
buffer, buffer_len TSRMLS_CC );
// save the parameter to be adjusted and/or converted after the results are processed
sqlsrv_output_param output_param( param_z, encoding, param_num, buffer_len );
sqlsrv_output_param output_param( param_ref, encoding, param_num, static_cast<SQLUINTEGER>( buffer_len ));
save_output_param_for_later( stmt, output_param TSRMLS_CC );
// For output parameters, if we set the column_size to be same as the buffer_len,
// than if there is a truncation due to the data coming from the server being
// then if there is a truncation due to the data coming from the server being
// greater than the column_size, we don't get any truncation error. In order to
// avoid this silent truncation, we set the column_size to be "MAX" size for
// string types. This will guarantee that there is no silent truncation for
@ -561,11 +545,10 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int dire
{
SQLSRV_ASSERT( direction == SQL_PARAM_INPUT, "Invalid output param type. The driver layer should catch this." );
sqlsrv_stream stream_encoding( param_z, encoding );
HashTable* streams_ht = Z_ARRVAL_P( stmt->param_streams );
core::sqlsrv_zend_hash_index_update( *stmt, streams_ht, param_num, &stream_encoding, sizeof( stream_encoding )
TSRMLS_CC );
HashTable* streams_ht = Z_ARRVAL( stmt->param_streams );
core::sqlsrv_zend_hash_index_update_mem( *stmt, streams_ht, param_num, &stream_encoding, sizeof(stream_encoding) TSRMLS_CC );
buffer = reinterpret_cast<SQLPOINTER>( param_num );
zval_add_ref( &param_z ); // so that it doesn't go away while we're using it
Z_TRY_ADDREF_P( param_z ); // so that it doesn't go away while we're using it
buffer_len = 0;
ind_ptr = SQL_DATA_AT_EXEC;
}
@ -573,18 +556,23 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int dire
case IS_OBJECT:
{
SQLSRV_ASSERT( direction == SQL_PARAM_INPUT, "Invalid output param type. The driver layer should catch this." );
zval_auto_ptr function_z;
zval_auto_ptr buffer_z;
zval_auto_ptr format_z;
zval* params[1];
bool valid_class_name_found = false;
zval function_z;
zval buffer_z;
zval format_z;
zval params[1];
ZVAL_UNDEF( &function_z );
ZVAL_UNDEF( &buffer_z );
ZVAL_UNDEF( &format_z );
ZVAL_UNDEF( params );
zend_class_entry *class_entry = zend_get_class_entry( param_z TSRMLS_CC );
bool valid_class_name_found = false;
zend_class_entry *class_entry = Z_OBJCE_P( param_z TSRMLS_CC );
while( class_entry != NULL ) {
if( class_entry->name_length == DateTime::DATETIME_CLASS_NAME_LEN && class_entry->name != NULL &&
stricmp( class_entry->name, DateTime::DATETIME_CLASS_NAME ) == 0 ) {
if( class_entry->name->len == DateTime::DATETIME_CLASS_NAME_LEN && class_entry->name != NULL &&
stricmp( class_entry->name->val, DateTime::DATETIME_CLASS_NAME ) == 0 ) {
valid_class_name_found = true;
break;
}
@ -599,40 +587,37 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int dire
CHECK_CUSTOM_ERROR( !valid_class_name_found, stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, param_num + 1 ) {
throw core::CoreException();
}
ALLOC_INIT_ZVAL( buffer_z );
ALLOC_INIT_ZVAL( function_z );
ALLOC_INIT_ZVAL( format_z );
// if the user specifies the 'date' sql type, giving it the normal format will cause a 'date overflow error'
// meaning there is too much information in the character string. If the user specifies the 'datetimeoffset'
// sql type, it lacks the timezone.
if( sql_type == SQL_SS_TIMESTAMPOFFSET ) {
ZVAL_STRINGL( format_z, const_cast<char*>( DateTime::DATETIMEOFFSET_FORMAT ),
DateTime::DATETIMEOFFSET_FORMAT_LEN, 1 /* dup */ );
core::sqlsrv_zval_stringl( &format_z, const_cast<char*>( DateTime::DATETIMEOFFSET_FORMAT ),
DateTime::DATETIMEOFFSET_FORMAT_LEN );
}
else if( sql_type == SQL_TYPE_DATE ) {
ZVAL_STRINGL( format_z, const_cast<char*>( DateTime::DATE_FORMAT ), DateTime::DATE_FORMAT_LEN, 1 /* dup */ );
core::sqlsrv_zval_stringl( &format_z, const_cast<char*>( DateTime::DATE_FORMAT ), DateTime::DATE_FORMAT_LEN );
}
else {
ZVAL_STRINGL( format_z, const_cast<char*>( DateTime::DATETIME_FORMAT ), DateTime::DATETIME_FORMAT_LEN,
1 /* dup */);
core::sqlsrv_zval_stringl( &format_z, const_cast<char*>( DateTime::DATETIME_FORMAT ), DateTime::DATETIME_FORMAT_LEN );
}
// call the DateTime::format member function to convert the object to a string that SQL Server understands
ZVAL_STRINGL( function_z, "format", sizeof( "format" ) - 1, 1 );
core::sqlsrv_zval_stringl( &function_z, "format", sizeof( "format" ) - 1 );
params[0] = format_z;
// This is equivalent to the PHP code: $param_z->format( $format_z ); where param_z is the
// DateTime object and $format_z is the format string.
int zr = call_user_function( EG( function_table ), &param_z, function_z, buffer_z, 1, params TSRMLS_CC );
int zr = call_user_function( EG( function_table ), param_z, &function_z, &buffer_z, 1, params TSRMLS_CC );
zend_string_release( Z_STR( format_z ));
zend_string_release( Z_STR( function_z ));
CHECK_CUSTOM_ERROR( zr == FAILURE, stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, param_num + 1 ) {
throw core::CoreException();
}
buffer = Z_STRVAL_P( buffer_z );
zr = add_next_index_zval( stmt->param_datetime_buffers, buffer_z );
buffer = Z_STRVAL( buffer_z );
zr = add_next_index_zval( &( stmt->param_datetime_buffers ), &buffer_z );
CHECK_CUSTOM_ERROR( zr == FAILURE, stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, param_num + 1 ) {
throw core::CoreException();
}
buffer_len = Z_STRLEN_P( buffer_z );
buffer_z.transferred();
buffer_len = Z_STRLEN( buffer_z ) - 1;
ind_ptr = buffer_len;
break;
}
@ -649,9 +634,8 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int dire
ind_ptr = SQL_NULL_DATA;
}
core::SQLBindParameter( stmt, param_num + 1, direction, c_type, sql_type, column_size, decimal_digits, buffer, buffer_len,
&ind_ptr TSRMLS_CC );
core::SQLBindParameter( stmt, param_num + 1, direction,
c_type, sql_type, column_size, decimal_digits, buffer, buffer_len, &ind_ptr TSRMLS_CC );
}
catch( core::CoreException& e ) {
stmt->free_param_data( TSRMLS_C );
@ -670,13 +654,13 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int dire
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;
@ -712,17 +696,24 @@ void core_sqlsrv_execute( sqlsrv_stmt* stmt TSRMLS_DC, const char* sql, int sql_
stmt->executed = true;
// if all the data has been sent and no data was returned then finalize the output parameters
if( stmt->send_streams_at_exec && (r == SQL_NO_DATA || !core_sqlsrv_has_any_result( stmt TSRMLS_CC ))) {
if( stmt->send_streams_at_exec && ( r == SQL_NO_DATA || !core_sqlsrv_has_any_result( stmt TSRMLS_CC ))) {
finalize_output_parameters( stmt TSRMLS_CC );
}
// stream parameters are sent, clean the Hashtable
if ( stmt->send_streams_at_exec ) {
zend_hash_clean( Z_ARRVAL( stmt->param_streams ));
}
}
catch( core::CoreException& e ) {
// if the statement executed but failed in a subsequent operation before returning,
// we need to cancel the statement
if( stmt->executed ) {
// we need to cancel the statement and deref the output and stream parameters
if ( stmt->send_streams_at_exec ) {
zend_hash_clean( Z_ARRVAL( stmt->output_params ));
zend_hash_clean( Z_ARRVAL( stmt->param_streams ));
}
if( stmt->executed ) {
SQLCancel( stmt->handle() );
// stmt->executed = false; should this be reset if something fails?
}
@ -742,7 +733,7 @@ void core_sqlsrv_execute( sqlsrv_stmt* stmt TSRMLS_DC, const char* sql, int sql_
// Nothing, exception thrown if an error. stmt->past_fetch_end is set to true if the
// user scrolls past a non-scrollable result set
bool core_sqlsrv_fetch( sqlsrv_stmt* stmt, SQLSMALLINT fetch_orientation, SQLLEN fetch_offset TSRMLS_DC )
bool core_sqlsrv_fetch( sqlsrv_stmt* stmt, SQLSMALLINT fetch_orientation, SQLULEN fetch_offset TSRMLS_DC )
{
// pre-condition check
SQLSRV_ASSERT( fetch_orientation >= SQL_FETCH_NEXT || fetch_orientation <= SQL_FETCH_RELATIVE,
@ -751,7 +742,7 @@ bool core_sqlsrv_fetch( sqlsrv_stmt* stmt, SQLSMALLINT fetch_orientation, SQLLEN
try {
// clear the field cache of the previous fetch
zend_hash_clean( Z_ARRVAL_P( stmt->field_cache ));
zend_hash_clean( Z_ARRVAL( stmt->field_cache ));
CHECK_CUSTOM_ERROR( !stmt->executed, stmt, SQLSRV_ERROR_STATEMENT_NOT_EXECUTED ) {
throw core::CoreException();
@ -879,101 +870,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( zend_hash_index_find( Z_ARRVAL_P( stmt->field_cache ), field_index, (void**) &cached ) == SUCCESS ) {
// 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;
}
try {
sqlsrv_phptype sqlsrv_php_type = sqlsrv_php_type_in;
// close the stream to release the resource
close_active_stream(stmt TSRMLS_CC);
SQLLEN sql_field_type = 0;
SQLLEN sql_field_len = 0;
// 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 {
// 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();
}
field_value = sqlsrv_malloc( cached->len, sizeof( char ), 1 );
memcpy_s( field_value, ( cached->len * sizeof( char )), 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 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( zend_hash_index_find( Z_ARRVAL_P( stmt->field_cache ), i, (void**) &cached ) == FAILURE,
"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;
}
}
}
sqlsrv_phptype sqlsrv_php_type = sqlsrv_php_type_in;
// 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 );
SQLLEN sql_field_type = 0;
SQLLEN sql_field_len = 0;
// Get the corresponding php type from the sql type.
sqlsrv_php_type = stmt->sql_type_to_php_type( sql_field_type, sql_field_len, prefer_string );
}
// 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();
}
// 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 );
// 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;
}
}
}
// Retrieve the data
core_get_field_common( stmt, field_index, sqlsrv_php_type, field_value, field_len TSRMLS_CC );
// 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 ) {
// 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( *stmt, Z_ARRVAL_P( stmt->field_cache ), field_index, &cache,
sizeof( field_cache ) 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 );
catch( core::CoreException& e) {
throw e;
}
// 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
@ -1025,7 +1015,7 @@ void core_sqlsrv_next_result( sqlsrv_stmt* stmt TSRMLS_DC, bool finalize_output_
if( r == SQL_NO_DATA ) {
if( stmt->output_params && finalize_output_params ) {
if( &(stmt->output_params) && finalize_output_params ) {
// if we're finished processing result sets, handle the output parameters
finalize_output_parameters( stmt TSRMLS_CC );
}
@ -1054,28 +1044,28 @@ void core_sqlsrv_next_result( sqlsrv_stmt* stmt TSRMLS_DC, bool finalize_output_
// Returns:
// Nothing, exception thrown if problem occurs
void core_sqlsrv_post_param( sqlsrv_stmt* stmt, unsigned int param_num, zval* param_z TSRMLS_DC )
void core_sqlsrv_post_param( sqlsrv_stmt* stmt, zend_ulong param_num, zval* param_z TSRMLS_DC )
{
SQLSRV_ASSERT( Z_TYPE_P( stmt->param_input_strings ) == IS_ARRAY, "Statement input parameter UTF-16 buffers array invalid." );
SQLSRV_ASSERT( Z_TYPE_P( stmt->param_streams ) == IS_ARRAY, "Statement input parameter streams array invalid." );
SQLSRV_ASSERT( Z_TYPE( stmt->param_input_strings ) == IS_ARRAY, "Statement input parameter UTF-16 buffers array invalid." );
SQLSRV_ASSERT( Z_TYPE( stmt->param_streams ) == IS_ARRAY, "Statement input parameter streams array invalid." );
// if the parameter was an input string, delete it from the array holding input parameter strings
if( zend_hash_index_exists( Z_ARRVAL_P( stmt->param_input_strings ), param_num )) {
core::sqlsrv_zend_hash_index_del( *stmt, Z_ARRVAL_P( stmt->param_input_strings ), param_num TSRMLS_CC );
if( zend_hash_index_exists( Z_ARRVAL( stmt->param_input_strings ), param_num )) {
core::sqlsrv_zend_hash_index_del( *stmt, Z_ARRVAL( stmt->param_input_strings ), param_num TSRMLS_CC );
}
// if the parameter was an input stream, decrement our reference to it and delete it from the array holding input streams
// PDO doesn't need the reference count, but sqlsrv does since the stream can be live after sqlsrv_execute by sending it
// with sqlsrv_send_stream_data.
if( zend_hash_index_exists( Z_ARRVAL_P( stmt->param_streams ), param_num )) {
sqlsrv_stream* stream_encoding;
zend_hash_index_find( Z_ARRVAL_P( stmt->param_streams ), param_num, (void**) &stream_encoding );
core::sqlsrv_zend_hash_index_del( *stmt, Z_ARRVAL_P( stmt->param_streams ), param_num TSRMLS_CC );
if( zend_hash_index_exists( Z_ARRVAL( stmt->param_streams ), param_num )) {
sqlsrv_stream* stream_encoding = NULL;
stream_encoding = reinterpret_cast<sqlsrv_stream*>(zend_hash_index_find_ptr(Z_ARRVAL(stmt->param_streams), param_num));
core::sqlsrv_zend_hash_index_del( *stmt, Z_ARRVAL( stmt->param_streams ), param_num TSRMLS_CC );
}
}
//Calls SQLSetStmtAttr to set a cursor.
void core_sqlsrv_set_scrollable( sqlsrv_stmt* stmt, unsigned int cursor_type TSRMLS_DC )
void core_sqlsrv_set_scrollable( sqlsrv_stmt* stmt, unsigned long cursor_type TSRMLS_DC )
{
try {
@ -1129,7 +1119,7 @@ void core_sqlsrv_set_buffered_query_limit( sqlsrv_stmt* stmt, zval* value_z TSRM
core_sqlsrv_set_buffered_query_limit( stmt, Z_LVAL_P( value_z ) TSRMLS_CC );
}
void core_sqlsrv_set_buffered_query_limit( sqlsrv_stmt* stmt, long limit TSRMLS_DC )
void core_sqlsrv_set_buffered_query_limit( sqlsrv_stmt* stmt, SQLLEN limit TSRMLS_DC )
{
if( limit <= 0 ) {
@ -1154,7 +1144,7 @@ void core_sqlsrv_set_query_timeout( sqlsrv_stmt* stmt, zval* value_z TSRMLS_DC )
THROW_CORE_ERROR( stmt, SQLSRV_ERROR_INVALID_QUERY_TIMEOUT_VALUE, Z_STRVAL_P( value_z ) );
}
core_sqlsrv_set_query_timeout( stmt, Z_LVAL_P( value_z ) TSRMLS_CC );
core_sqlsrv_set_query_timeout( stmt, static_cast<long>( Z_LVAL_P( value_z )) TSRMLS_CC );
}
catch( core::CoreException& ) {
throw;
@ -1169,7 +1159,7 @@ void core_sqlsrv_set_query_timeout( sqlsrv_stmt* stmt, long timeout TSRMLS_DC )
DEBUG_SQLSRV_ASSERT( timeout >= 0 , "core_sqlsrv_set_query_timeout: The value of query timeout cannot be less than 0." );
// set the statement attribute
core::SQLSetStmtAttr( stmt, SQL_ATTR_QUERY_TIMEOUT, reinterpret_cast<SQLPOINTER>( timeout ), SQL_IS_UINTEGER TSRMLS_CC );
core::SQLSetStmtAttr( stmt, SQL_ATTR_QUERY_TIMEOUT, reinterpret_cast<SQLPOINTER>( (SQLLEN)timeout ), SQL_IS_UINTEGER TSRMLS_CC );
// a query timeout of 0 indicates "no timeout", which means that lock_timeout should also be set to "no timeout" which
// is represented by -1.
@ -1232,7 +1222,7 @@ bool core_sqlsrv_send_stream_packet( sqlsrv_stmt* stmt TSRMLS_DC )
// get the stream from the zval we bound
php_stream* param_stream = NULL;
core::sqlsrv_php_stream_from_zval_no_verify( *stmt, param_stream, &stmt->current_stream.stream_z TSRMLS_CC );
core::sqlsrv_php_stream_from_zval_no_verify( *stmt, param_stream, stmt->current_stream.stream_z TSRMLS_CC );
// if we're at the end, then release our current parameter
if( php_stream_eof( param_stream )) {
@ -1248,9 +1238,16 @@ bool core_sqlsrv_send_stream_packet( sqlsrv_stmt* stmt TSRMLS_DC )
// read the data from the stream, send it via SQLPutData and track how much we've sent.
else {
char buffer[ PHP_STREAM_BUFFER_SIZE + 1 ];
size_t buffer_size = sizeof( buffer ) - 3; // -3 to preserve enough space for a cut off UTF-8 character
size_t read = php_stream_read( param_stream, buffer, buffer_size );
stmt->current_stream_read += read;
std::size_t buffer_size = sizeof( buffer ) - 3; // -3 to preserve enough space for a cut off UTF-8 character
std::size_t read = php_stream_read( param_stream, buffer, buffer_size );
if (read > UINT_MAX)
{
LOG(SEV_ERROR, "PHP stream: buffer length exceeded.");
throw core::CoreException();
}
stmt->current_stream_read += static_cast<unsigned int>( read );
if( read > 0 ) {
// if this is a UTF-8 stream, then we will use the UTF-8 encoding to determine if we're in the middle of a character
// then read in the appropriate number more bytes and then retest the string. This way we try at most to convert it
@ -1264,7 +1261,7 @@ bool core_sqlsrv_send_stream_packet( sqlsrv_stmt* stmt TSRMLS_DC )
wchar_t wbuffer[ PHP_STREAM_BUFFER_SIZE + 1 ];
// buffer_size is the # of wchars. Since it set to stmt->param_buffer_size / 2, this is accurate
int wsize = MultiByteToWideChar( stmt->current_stream.encoding, MB_ERR_INVALID_CHARS,
buffer, read, wbuffer, sizeof( wbuffer ) / sizeof( wchar_t ));
buffer, static_cast<int>( read ), wbuffer, static_cast<int>( sizeof( wbuffer ) / sizeof( wchar_t )));
if( wsize == 0 && GetLastError() == ERROR_NO_UNICODE_TRANSLATION ) {
// this will calculate how many bytes were cut off from the last UTF-8 character and read that many more
@ -1280,7 +1277,7 @@ bool core_sqlsrv_send_stream_packet( sqlsrv_stmt* stmt TSRMLS_DC )
}
// try the conversion again with the complete character
wsize = MultiByteToWideChar( stmt->current_stream.encoding, MB_ERR_INVALID_CHARS,
buffer, read + new_read, wbuffer, sizeof( wbuffer ) / sizeof( wchar_t ));
buffer, static_cast<int>( read + new_read ), wbuffer, static_cast<int>( sizeof( wbuffer ) / sizeof( wchar_t )));
// something else must be wrong if it failed
CHECK_CUSTOM_ERROR( wsize == 0, stmt, SQLSRV_ERROR_INPUT_STREAM_ENCODING_TRANSLATE,
get_last_error_message( ERROR_NO_UNICODE_TRANSLATION )) {
@ -1308,7 +1305,6 @@ bool core_sqlsrv_send_stream_packet( sqlsrv_stmt* stmt TSRMLS_DC )
return true;
}
void stmt_option_functor::operator()( sqlsrv_stmt* /*stmt*/, stmt_option const* /*opt*/, zval* /*value_z*/ TSRMLS_DC )
{
TSRMLS_C;
@ -1335,23 +1331,23 @@ void stmt_option_buffered_query_limit:: operator()( sqlsrv_stmt* stmt, stmt_opti
// internal function to release the active stream. Called by each main API function
// that will alter the statement and cancel any retrieval of data from a stream.
void close_active_stream( __inout sqlsrv_stmt* stmt TSRMLS_DC )
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." );
}
@ -1359,7 +1355,7 @@ void close_active_stream( __inout sqlsrv_stmt* stmt TSRMLS_DC )
namespace {
bool is_streamable_type( SQLINTEGER sql_type )
bool is_streamable_type( SQLLEN sql_type )
{
switch( sql_type ) {
case SQL_CHAR:
@ -1378,7 +1374,7 @@ bool is_streamable_type( SQLINTEGER sql_type )
return false;
}
void calc_string_size( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, SQLLEN sql_type, __out SQLLEN& size TSRMLS_DC )
void calc_string_size( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, SQLLEN sql_type, _Out_ SQLLEN& size TSRMLS_DC )
{
try {
@ -1474,200 +1470,204 @@ 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 )
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 {
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_auto_ptr field_value_temp_z;
zval_auto_ptr return_value_z;
zval_auto_ptr function_z;
zval* params[1];
ALLOC_INIT_ZVAL( field_value_temp_z );
ALLOC_INIT_ZVAL( function_z );
ALLOC_INIT_ZVAL( return_value_z );
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 );
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 );
CHECK_SQL_ERROR_OR_WARNING( 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 == 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
ZVAL_STRINGL( field_value_temp_z, field_value_temp, *field_len, 1 );
ZVAL_STRINGL( function_z, "date_create", sizeof("date_create") -1, 1 );
params[0] = field_value_temp_z;
if( *field_len == SQL_NULL_DATA ) {
field_value = NULL;
break;
}
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 = field_value_temp;
field_value_temp.transferred();
break;
}
*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;
php_stream* stream = NULL;
sqlsrv_stream* ss = NULL;
ALLOC_INIT_ZVAL( return_value_z );
SQLINTEGER sql_type;
case SQLSRV_PHPTYPE_STRING:
{
get_field_as_string( stmt, field_index, sqlsrv_php_type, field_value, field_len TSRMLS_CC );
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 );
// 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;
CHECK_CUSTOM_ERROR( !stream, stmt, SQLSRV_ERROR_STREAM_CREATE ) {
throw core::CoreException();
}
ZVAL_UNDEF( &field_value_temp_z );
ZVAL_UNDEF( &function_z );
ZVAL_UNDEF( params );
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 );
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 );
// turn our stream into a zval to be returned
php_stream_to_zval( stream, return_value_z );
CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) {
throw core::CoreException();
}
// 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();
zval_auto_ptr return_value_z;
return_value_z = ( zval * )sqlsrv_malloc( sizeof( zval ));
ZVAL_UNDEF( return_value_z );
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;
}
case SQLSRV_PHPTYPE_NULL:
*field_value = NULL;
*field_len = 0;
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;
default:
DIE( "core_get_field_common: Unexpected sqlsrv_phptype provided" );
break;
}
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);
}
// 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;
}
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:
{
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 );
zval_auto_ptr return_value_z;
return_value_z = ( zval * )sqlsrv_malloc( sizeof( zval ));
ZVAL_UNDEF( return_value_z );
// 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;
}
}
// check_for_next_stream_parameter
// see if there is another stream to be sent. Returns true and sets the stream as current in the statement structure, otherwise
// returns false
bool check_for_next_stream_parameter( __inout sqlsrv_stmt* stmt TSRMLS_DC )
bool check_for_next_stream_parameter( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
{
int stream_index = 0;
SQLRETURN r = SQL_SUCCESS;
sqlsrv_stream* stream_encoding;
sqlsrv_stream* stream_encoding = NULL;
zval* param_z = NULL;
// get the index into the streams_ht from the parameter data we set in core_sqlsrv_bind_param
@ -1679,11 +1679,11 @@ bool check_for_next_stream_parameter( __inout sqlsrv_stmt* stmt TSRMLS_DC )
return false;
}
HashTable* streams_ht = Z_ARRVAL_P( stmt->param_streams );
HashTable* streams_ht = Z_ARRVAL( stmt->param_streams );
// pull out the sqlsrv_encoding struct
int zr = zend_hash_index_find( streams_ht, stream_index, (void**) &stream_encoding );
SQLSRV_ASSERT( zr == SUCCESS, "Stream parameter does not exist" ); // if the index isn't in the hash, that's a serious error
stream_encoding = reinterpret_cast<sqlsrv_stream*>(zend_hash_index_find_ptr(streams_ht, stream_index));
SQLSRV_ASSERT(stream_encoding != NULL, "Stream parameter does not exist"); // if the index isn't in the hash, that's a serious error
param_z = stream_encoding->stream_z;
@ -1704,18 +1704,26 @@ bool convert_input_param_to_utf16( zval* input_param_z, zval* converted_param_z
"convert_input_param_z called with invalid parameter states" );
const char* buffer = Z_STRVAL_P( input_param_z );
int buffer_len = Z_STRLEN_P( input_param_z );
std::size_t buffer_len = Z_STRLEN_P( input_param_z );
int wchar_size;
if (buffer_len > INT_MAX)
{
LOG(SEV_ERROR, "Convert input parameter to utf16: buffer length exceeded.");
throw core::CoreException();
}
// if the string is empty, then just return that the conversion succeeded as
// MultiByteToWideChar will "fail" on an empty string.
if( buffer_len == 0 ) {
ZVAL_STRINGL( converted_param_z, "", 0, 1 );
core::sqlsrv_zval_stringl( converted_param_z, "", 0 );
return true;
}
// if the parameter is an input parameter, calc the size of the necessary buffer from the length of the string
wchar_size = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, reinterpret_cast<LPCSTR>( buffer ), buffer_len, NULL, 0 );
wchar_size = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS,
reinterpret_cast<LPCSTR>( buffer ), static_cast<int>( buffer_len ), NULL, 0 );
// if there was a problem determining the size of the string, return false
if( wchar_size == 0 ) {
return false;
@ -1724,7 +1732,7 @@ bool convert_input_param_to_utf16( zval* input_param_z, zval* converted_param_z
wbuffer = reinterpret_cast<wchar_t*>( sqlsrv_malloc( (wchar_size + 1) * sizeof( wchar_t ) ));
// convert the utf-8 string to a wchar string in the new buffer
int r = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, reinterpret_cast<LPCSTR>( buffer ),
buffer_len, wbuffer, wchar_size );
static_cast<int>( buffer_len ), wbuffer, wchar_size );
// if there was a problem converting the string, then free the memory and return false
if( r == 0 ) {
return false;
@ -1732,8 +1740,9 @@ bool convert_input_param_to_utf16( zval* input_param_z, zval* converted_param_z
// null terminate the string, set the size within the zval, and return success
wbuffer[ wchar_size ] = L'\0';
ZVAL_STRINGL( converted_param_z, reinterpret_cast<char*>( wbuffer.get() ),
wchar_size * sizeof( wchar_t ), 0 );
core::sqlsrv_zval_stringl( converted_param_z, reinterpret_cast<char*>( wbuffer.get() ),
wchar_size * sizeof( wchar_t ) );
sqlsrv_free(wbuffer);
wbuffer.transferred();
return true;
@ -1741,7 +1750,7 @@ bool convert_input_param_to_utf16( zval* input_param_z, zval* converted_param_z
// returns the ODBC C type constant that matches the PHP type and encoding given
SQLSMALLINT default_c_type( sqlsrv_stmt* stmt, unsigned int paramno, zval const* param_z, SQLSRV_ENCODING encoding TSRMLS_DC )
SQLSMALLINT default_c_type( sqlsrv_stmt* stmt, SQLULEN paramno, zval const* param_z, SQLSRV_ENCODING encoding TSRMLS_DC )
{
SQLSMALLINT sql_c_type = SQL_UNKNOWN_TYPE;
int php_type = Z_TYPE_P( param_z );
@ -1762,9 +1771,18 @@ SQLSMALLINT default_c_type( sqlsrv_stmt* stmt, unsigned int paramno, zval const*
break;
}
break;
case IS_BOOL:
case IS_TRUE:
case IS_FALSE:
case IS_LONG:
sql_c_type = SQL_C_LONG;
//ODBC 64-bit long and integer type are 4 byte values.
if ( ( Z_LVAL_P( param_z ) < INT_MIN ) || ( Z_LVAL_P( param_z ) > INT_MAX ) )
{
sql_c_type = SQL_C_SBIGINT;
}
else
{
sql_c_type = SQL_C_SLONG;
}
break;
case IS_DOUBLE:
sql_c_type = SQL_C_DOUBLE;
@ -1803,12 +1821,11 @@ SQLSMALLINT default_c_type( sqlsrv_stmt* stmt, unsigned int paramno, zval const*
// given a zval and encoding, determine the appropriate sql type
void default_sql_type( sqlsrv_stmt* stmt, unsigned int paramno, zval* param_z, SQLSRV_ENCODING encoding,
__out SQLSMALLINT& sql_type TSRMLS_DC )
void default_sql_type( sqlsrv_stmt* stmt, SQLULEN paramno, zval* param_z, SQLSRV_ENCODING encoding,
_Out_ SQLSMALLINT& sql_type TSRMLS_DC )
{
sql_type = SQL_UNKNOWN_TYPE;
int php_type = Z_TYPE_P( param_z );
int php_type = Z_TYPE_P(param_z);
switch( php_type ) {
case IS_NULL:
@ -1825,9 +1842,19 @@ void default_sql_type( sqlsrv_stmt* stmt, unsigned int paramno, zval* param_z, S
break;
}
break;
case IS_BOOL:
case IS_TRUE:
case IS_FALSE:
case IS_LONG:
sql_type = SQL_INTEGER;
//ODBC 64-bit long and integer type are 4 byte values.
if ( ( Z_LVAL_P( param_z ) < INT_MIN ) || ( Z_LVAL_P( param_z ) > INT_MAX ) )
{
sql_type = SQL_BIGINT;
}
else
{
sql_type = SQL_INTEGER;
}
break;
case IS_DOUBLE:
sql_type = SQL_FLOAT;
@ -1874,7 +1901,7 @@ void default_sql_type( sqlsrv_stmt* stmt, unsigned int paramno, zval* param_z, S
// given a zval and encoding, determine the appropriate column size, and decimal scale (if appropriate)
void default_sql_size_and_scale( sqlsrv_stmt* stmt, unsigned int paramno, zval* param_z, SQLSRV_ENCODING encoding,
__out SQLULEN& column_size, __out SQLSMALLINT& decimal_digits TSRMLS_DC )
_Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits TSRMLS_DC )
{
int php_type = Z_TYPE_P( param_z );
column_size = 0;
@ -1886,19 +1913,21 @@ void default_sql_size_and_scale( sqlsrv_stmt* stmt, unsigned int paramno, zval*
column_size = 1;
break;
// size is not necessary for these types, they are inferred by ODBC
case IS_BOOL:
case IS_TRUE:
case IS_FALSE:
case IS_LONG:
case IS_DOUBLE:
case IS_RESOURCE:
break;
case IS_STRING:
{
SQLULEN byte_len = Z_STRLEN_P( param_z ) * ((encoding == SQLSRV_ENCODING_UTF8) ? sizeof( wchar_t ) : sizeof( char ));
size_t char_size = ( encoding == SQLSRV_ENCODING_UTF8 ) ? sizeof( wchar_t ) : sizeof( char );
SQLULEN byte_len = Z_STRLEN_P( param_z ) * char_size;
if( byte_len > SQL_SERVER_MAX_FIELD_SIZE ) {
column_size = SQL_SERVER_MAX_TYPE_SIZE;
}
else {
column_size = Z_STRLEN_P( param_z );
column_size = SQL_SERVER_MAX_FIELD_SIZE / char_size;
}
break;
}
@ -1924,13 +1953,14 @@ void default_sql_size_and_scale( sqlsrv_stmt* stmt, unsigned int paramno, zval*
}
}
void field_cache_dtor( void* data )
void field_cache_dtor( zval* data_z )
{
field_cache* cache = reinterpret_cast<field_cache*>( data );
field_cache* cache = static_cast<field_cache*>( Z_PTR_P( data_z ));
if( cache->value )
{
sqlsrv_free( cache->value );
}
sqlsrv_free( cache );
}
@ -1942,325 +1972,329 @@ void field_cache_dtor( void* data )
void finalize_output_parameters( sqlsrv_stmt* stmt TSRMLS_DC )
{
if( stmt->output_params == NULL )
if( Z_ISUNDEF(stmt->output_params) )
return;
bool converted = true;
HashTable* params_ht = Z_ARRVAL_P( stmt->output_params );
HashTable* params_ht = Z_ARRVAL( stmt->output_params );
zend_ulong index = -1;
zend_string* key = NULL;
void* output_param_temp = NULL;
for( zend_hash_internal_pointer_reset( params_ht );
zend_hash_has_more_elements( params_ht ) == SUCCESS;
zend_hash_move_forward( params_ht ) ) {
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:
{
// adjust the length of the string to the value returned by SQLBindParameter in the ind_ptr parameter
char* str = Z_STRVAL_P( value_z );
SQLLEN str_len = stmt->param_ind_ptrs[ output_param->param_num ];
if( str_len == SQL_NULL_DATA ) {
zend_string_release( Z_STR_P( value_z ));
ZVAL_NULL( value_z );
continue;
}
sqlsrv_output_param *output_param;
core::sqlsrv_zend_hash_get_current_data( *stmt, params_ht, (void**) &output_param TSRMLS_CC );
switch( Z_TYPE_P( output_param->param_z )) {
case IS_STRING:
{
// adjust the length of the string to the value returned by SQLBindParameter in the ind_ptr parameter
char* str = Z_STRVAL_P( output_param->param_z );
SQLLEN str_len = stmt->param_ind_ptrs[ output_param->param_num ];
if( str_len == SQL_NULL_DATA ) {
ZVAL_NULL( output_param->param_z );
continue;
}
// if there was more to output than buffer size to hold it, then throw a truncation error
int null_size = 0;
switch( output_param->encoding ) {
case SQLSRV_ENCODING_UTF8:
null_size = sizeof( wchar_t ); // string isn't yet converted to UTF-8, still UTF-16
break;
case SQLSRV_ENCODING_SYSTEM:
null_size = 1;
break;
case SQLSRV_ENCODING_BINARY:
null_size = 0;
break;
default:
SQLSRV_ASSERT( false, "Invalid encoding in output_param structure." );
break;
}
CHECK_CUSTOM_ERROR( str_len > ( output_param->original_buffer_len - null_size ), stmt,
SQLSRV_ERROR_OUTPUT_PARAM_TRUNCATED, output_param->param_num + 1 ) {
throw core::CoreException();
}
// if it's not in the 8 bit encodings, then it's in UTF-16
if( output_param->encoding != SQLSRV_ENCODING_CHAR && output_param->encoding != SQLSRV_ENCODING_BINARY ) {
bool converted = convert_string_from_utf16_inplace( output_param->encoding, &str, str_len );
CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_OUTPUT_PARAM_ENCODING_TRANSLATE, get_last_error_message()) {
throw core::CoreException();
}
}
else if( output_param->encoding == SQLSRV_ENCODING_BINARY && str_len < output_param->original_buffer_len ) {
// ODBC doesn't null terminate binary encodings, but PHP complains if a string isn't null terminated
// so we do that here if the length of the returned data is less than the original allocation. The
// original allocation null terminates the buffer already.
str[ str_len ] = '\0';
}
// set the string length
ZVAL_STRINGL( output_param->param_z, str, str_len, 0 );
}
// if there was more to output than buffer size to hold it, then throw a truncation error
int null_size = 0;
switch( output_param->encoding ) {
case SQLSRV_ENCODING_UTF8:
null_size = sizeof( wchar_t ); // string isn't yet converted to UTF-8, still UTF-16
break;
case IS_LONG:
// for a long or a float, simply check if NULL was returned and set the parameter to a PHP null if so
if( stmt->param_ind_ptrs[ output_param->param_num ] == SQL_NULL_DATA ) {
ZVAL_NULL( output_param->param_z );
}
else if( output_param->is_bool ) {
convert_to_boolean( output_param->param_z );
}
case SQLSRV_ENCODING_SYSTEM:
null_size = 1;
break;
case IS_DOUBLE:
// for a long or a float, simply check if NULL was returned and set the parameter to a PHP null if so
if( stmt->param_ind_ptrs[ output_param->param_num ] == SQL_NULL_DATA ) {
ZVAL_NULL( output_param->param_z );
}
case SQLSRV_ENCODING_BINARY:
null_size = 0;
break;
default:
DIE( "Illegal or unknown output parameter type. This should have been caught in core_sqlsrv_bind_parameter." );
SQLSRV_ASSERT( false, "Invalid encoding in output_param structure." );
break;
}
CHECK_CUSTOM_ERROR( str_len > ( output_param->original_buffer_len - null_size ), stmt,
SQLSRV_ERROR_OUTPUT_PARAM_TRUNCATED, output_param->param_num + 1 ) {
throw core::CoreException();
}
// if it's not in the 8 bit encodings, then it's in UTF-16
if( output_param->encoding != SQLSRV_ENCODING_CHAR && output_param->encoding != SQLSRV_ENCODING_BINARY ) {
bool converted = convert_zval_string_from_utf16(output_param->encoding, value_z, str_len);
CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_OUTPUT_PARAM_ENCODING_TRANSLATE, get_last_error_message()) {
throw core::CoreException();
}
}
else if( output_param->encoding == SQLSRV_ENCODING_BINARY && str_len < output_param->original_buffer_len ) {
// ODBC doesn't null terminate binary encodings, but PHP complains if a string isn't null terminated
// so we do that here if the length of the returned data is less than the original allocation. The
// original allocation null terminates the buffer already.
str[ str_len ] = '\0';
core::sqlsrv_zval_stringl(value_z, str, str_len);
}
else {
core::sqlsrv_zval_stringl(value_z, str, str_len);
}
}
}
break;
case IS_LONG:
// for a long or a float, simply check if NULL was returned and set the parameter to a PHP null if so
if( stmt->param_ind_ptrs[ output_param->param_num ] == SQL_NULL_DATA ) {
ZVAL_NULL( value_z );
}
else if( output_param->is_bool ) {
convert_to_boolean( value_z );
}
else
{
ZVAL_LONG( value_z, static_cast<int>( Z_LVAL_P( value_z )));
}
break;
case IS_DOUBLE:
// for a long or a float, simply check if NULL was returned and set the parameter to a PHP null if so
if( stmt->param_ind_ptrs[ output_param->param_num ] == SQL_NULL_DATA ) {
ZVAL_NULL( value_z );
}
break;
default:
DIE( "Illegal or unknown output parameter type. This should have been caught in core_sqlsrv_bind_parameter." );
break;
}
value_z = NULL;
} ZEND_HASH_FOREACH_END();
// empty the hash table since it's been processed
zend_hash_clean( Z_ARRVAL_P( stmt->output_params ));
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 == INT_MAX ||
sql_display_size == INT_MAX >> 1 || sql_display_size == UINT_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 ));
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 ) ) {
SQLINTEGER dummy_field_len;
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 );
// 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;
CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) {
throw core::CoreException();
}
do {
if( field_len_temp == SQL_NULL_DATA ) {
field_value = NULL;
sqlsrv_free( field_value_temp );
return;
}
SQLINTEGER 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;
if( r == SQL_SUCCESS_WITH_INFO ) {
// 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 );
SQLCHAR state[ SQL_SQLSTATE_BUFSIZE ];
SQLSMALLINT len;
// 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;
stmt->current_results->get_diag_field( 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len TSRMLS_CC );
if( r == SQL_SUCCESS_WITH_INFO ) {
core::SQLGetDiagField( stmt, 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len
TSRMLS_CC );
}
if( is_truncated_warning( state )) {
} while( r == SQL_SUCCESS_WITH_INFO && is_truncated_warning( state ));
}
else {
SQLLEN dummy_field_len;
// 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 );
// 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 ) {
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;
}
// reset the field_len_temp
field_len_temp = INITIAL_FIELD_STRING_LEN;
} // if( is_truncation_warning ( state ) )
else {
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) {
throw core::CoreException();
}
}
} // if( r == SQL_SUCCESS_WITH_INFO )
do {
SQLLEN initial_field_len = field_len_temp;
// Double the size.
field_len_temp *= 2;
if( sqlsrv_php_type.typeinfo.encoding == SQLSRV_ENCODING_UTF8 ) {
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 ) {
field_len_temp -= initial_field_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_len,
field_len_temp + extra, &dummy_field_len,
false /*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);
}
// 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_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( r == SQL_SUCCESS_WITH_INFO ) {
core::SQLGetDiagField( stmt, 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len
TSRMLS_CC );
}
if( field_len_temp == SQL_NULL_DATA ) {
*field_value = NULL;
sqlsrv_free( field_value_temp );
return;
}
if( sqlsrv_php_type.typeinfo.encoding == CP_UTF8 ) {
} while( r == SQL_SUCCESS_WITH_INFO && is_truncated_warning( state ));
}
else {
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 )
// 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 ));
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';
}
// We have already recieved INITIAL_FIELD_STRING_LEN size data.
field_len_temp -= INITIAL_FIELD_STRING_LEN;
catch( core::CoreException& ) {
// 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 );
*field_value = NULL;
*field_len = 0;
sqlsrv_free( field_value_temp );
throw;
}
catch ( ... ) {
if( dummy_field_len == SQL_NULL_DATA ) {
field_value = NULL;
sqlsrv_free( field_value_temp );
return;
}
*field_value = NULL;
*field_len = 0;
sqlsrv_free( field_value_temp );
throw;
}
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( is_truncation_warning ( state ) )
else {
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) {
throw core::CoreException();
}
}
} // if( r == SQL_SUCCESS_WITH_INFO )
if( sqlsrv_php_type.typeinfo.encoding == SQLSRV_ENCODING_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();
}
}
} // if ( sql_display_size == 0 || sql_display_size == LONG_MAX .. )
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;
}
}
@ -2268,7 +2302,7 @@ void get_field_as_string( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_ph
// return the option from the stmt_opts array that matches the key. If no option found,
// NULL is returned.
stmt_option const* get_stmt_option( sqlsrv_conn const* conn, unsigned long key, const stmt_option stmt_opts[] TSRMLS_DC )
stmt_option const* get_stmt_option( sqlsrv_conn const* conn, zend_ulong key, const stmt_option stmt_opts[] TSRMLS_DC )
{
for( int i = 0; stmt_opts[ i ].key != SQLSRV_STMT_OPTION_INVALID; ++i ) {
@ -2334,13 +2368,12 @@ bool is_valid_sqlsrv_phptype( sqlsrv_phptype type )
// string is place in the stmt->output_params. param_z is modified to hold the new buffer, and buffer, buffer_len and
// stmt->param_ind_ptrs are modified to hold the correct values for SQLBindParameter
void resize_output_buffer_if_necessary( sqlsrv_stmt* stmt, zval* param_z, unsigned int paramno, SQLSRV_ENCODING encoding,
void resize_output_buffer_if_necessary( sqlsrv_stmt* stmt, zval* param_z, SQLULEN paramno, SQLSRV_ENCODING encoding,
SQLSMALLINT c_type, SQLSMALLINT sql_type, SQLULEN column_size, SQLPOINTER& buffer,
SQLLEN& buffer_len TSRMLS_DC )
{
SQLSRV_ASSERT( column_size != SQLSRV_UNKNOWN_SIZE, "column size should be set to a known value." );
buffer_len = Z_STRLEN_P( param_z );
buffer = Z_STRVAL_P( param_z );
SQLLEN expected_len;
SQLLEN buffer_null_extra;
SQLLEN elem_size;
@ -2371,20 +2404,24 @@ void resize_output_buffer_if_necessary( sqlsrv_stmt* stmt, zval* param_z, unsign
// allocate enough space to ALWAYS include the NULL regardless of the type being retrieved since
// we set the last byte(s) to be NULL to avoid the debug build warning from the Zend engine about
// not having a NULL terminator on a string.
buffer = static_cast<char*>( sqlsrv_realloc( buffer, expected_len ));
buffer_len = expected_len; // set the buffer_len to the new allocation size (includes the null terminator taken out below)
zend_string* param_z_string = zend_string_realloc( Z_STR_P(param_z), expected_len, 0 );
// A zval string len doesn't include the null. This calculates the length it should be
// regardless of whether the ODBC type contains the NULL or not.
ZVAL_STRINGL( param_z, reinterpret_cast<char*>( buffer ), without_null_len, 0 );
// null terminate the string to avoid a warning in debug PHP builds
(static_cast<char*>(buffer))[ without_null_len ] = '\0';
}
ZSTR_VAL(param_z_string)[without_null_len] = '\0';
ZVAL_NEW_STR(param_z, param_z_string);
// buffer_len is the length passed to SQLBindParameter. It must contain the space for NULL in the
// buffer when retrieving anything but SQLSRV_ENC_BINARY/SQL_C_BINARY
buffer_len -= buffer_null_extra;
// buffer_len is the length passed to SQLBindParameter. It must contain the space for NULL in the
// buffer when retrieving anything but SQLSRV_ENC_BINARY/SQL_C_BINARY
buffer_len = Z_STRLEN_P(param_z) - buffer_null_extra;
// Zend string length doesn't include the null terminator
ZSTR_LEN(Z_STR_P(param_z)) -= elem_size;
}
buffer = Z_STRVAL_P(param_z);
// The StrLen_Ind_Ptr parameter of SQLBindParameter should contain the length of the data to send, which
// may be less than the size of the buffer since the output may be more than the input. If it is greater,
@ -2394,18 +2431,16 @@ void resize_output_buffer_if_necessary( sqlsrv_stmt* stmt, zval* param_z, unsign
}
}
// output parameters have their reference count incremented so that they do not disappear
// while the query is executed and processed. They are saved in the statement so that
// their reference count may be decremented later (after results are processed)
void save_output_param_for_later( sqlsrv_stmt* stmt, sqlsrv_output_param& param TSRMLS_DC )
{
HashTable* param_ht = Z_ARRVAL_P( stmt->output_params );
int paramno = param.param_num;
core::sqlsrv_zend_hash_index_update( *stmt, param_ht, paramno, &param, sizeof( param )
TSRMLS_CC );
zval_add_ref( &param.param_z ); // we have a reference to the param
HashTable* param_ht = Z_ARRVAL( stmt->output_params );
zend_ulong paramno = static_cast<zend_ulong>( param.param_num );
core::sqlsrv_zend_hash_index_update_mem(*stmt, param_ht, paramno, &param, sizeof( sqlsrv_output_param ));
Z_TRY_ADDREF_P( param.param_z ); // we have a reference to the param
}
@ -2418,19 +2453,19 @@ void send_param_streams( sqlsrv_stmt* stmt TSRMLS_DC )
// called by Zend for each parameter in the sqlsrv_stmt::output_params hash table when it is cleaned/destroyed
void sqlsrv_output_param_dtor( void* data )
void sqlsrv_output_param_dtor( zval* data )
{
sqlsrv_output_param *output_param = reinterpret_cast<sqlsrv_output_param*>( data );
zval_ptr_dtor( &output_param->param_z ); // undo the reference to the string we will no longer hold
sqlsrv_output_param *output_param = static_cast<sqlsrv_output_param*>( Z_PTR_P( data ));
zval_ptr_dtor( output_param->param_z ); // undo the reference to the string we will no longer hold
sqlsrv_free( output_param );
}
// called by Zend for each stream in the sqlsrv_stmt::param_streams hash table when it is cleaned/destroyed
void sqlsrv_stream_dtor( void* data )
void sqlsrv_stream_dtor( zval* data )
{
sqlsrv_stream* stream_encoding = reinterpret_cast<sqlsrv_stream*>( data );
zval_ptr_dtor( &stream_encoding->stream_z ); // undo the reference to the stream we will no longer hold
sqlsrv_stream* stream_encoding = static_cast<sqlsrv_stream*>( Z_PTR_P( data ));
zval_ptr_dtor( stream_encoding->stream_z ); // undo the reference to the stream we will no longer hold
sqlsrv_free( stream_encoding );
}
}

View file

@ -3,7 +3,7 @@
//
// Contents: Implementation of PHP streams for reading SQL Server data
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -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;
@ -48,10 +45,9 @@ int sqlsrv_stream_close( php_stream* stream, int /*close_handle*/ TSRMLS_DC )
// read from a sqlsrv stream into the buffer provided by Zend. The parameters for binary vs. char are
// set when sqlsrv_get_field is called by the user specifying which field type they want.
size_t sqlsrv_stream_read( php_stream* stream, __out_bcount(count) char* buf, size_t count TSRMLS_DC )
size_t sqlsrv_stream_read( php_stream* stream, _Out_writes_bytes_(count) char* buf, size_t count TSRMLS_DC )
{
SQLINTEGER read = 0;
SQLLEN read = 0;
SQLSMALLINT c_type = SQL_C_CHAR;
char* get_data_buffer = buf;
sqlsrv_malloc_auto_ptr<char> temp_buf;
@ -145,21 +141,28 @@ size_t sqlsrv_stream_read( php_stream* stream, __out_bcount(count) char* buf, si
}
// if the encoding is UTF-8
if( c_type == SQL_C_WCHAR ) {
count *= 2; // undo the shift to use the full buffer
if (c_type == SQL_C_WCHAR) {
// flags set to 0 by default, which means that any invalid characters are dropped rather than causing
// an error. This happens only on XP.
DWORD flags = 0;
count *= 2; // undo the shift to use the full buffer
// flags set to 0 by default, which means that any invalid characters are dropped rather than causing
// an error. This happens only on XP.
DWORD flags = 0;
// convert to UTF-8
if (g_osversion.dwMajorVersion >= SQLSRV_OS_VISTA_OR_LATER) {
// Vista (and later) will detect invalid UTF-16 characters and raise an error.
flags = WC_ERR_INVALID_CHARS;
}
if ( count > INT_MAX || (read >> 1) > INT_MAX )
{
LOG(SEV_ERROR, "UTF-16 (wide character) string mapping: buffer length exceeded.");
throw core::CoreException();
}
// convert to UTF-8
if( g_osversion.dwMajorVersion >= SQLSRV_OS_VISTA_OR_LATER ) {
// Vista (and later) will detect invalid UTF-16 characters and raise an error.
flags = WC_ERR_INVALID_CHARS;
}
int enc_len = WideCharToMultiByte( ss->encoding, flags, reinterpret_cast<LPCWSTR>( temp_buf.get() ),
read >> 1, buf, count, NULL, NULL );
static_cast<int>(read >> 1), buf, static_cast<int>(count), NULL, NULL );
if( enc_len == 0 ) {
@ -200,13 +203,8 @@ php_stream_ops sqlsrv_stream_ops = {
// open a stream and return the sqlsrv_stream_ops function table as part of the
// return value. There is only one valid way to open a stream, using sqlsrv_get_field on
// certain field types. A sqlsrv stream may only be opened in read mode.
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 6
static php_stream* sqlsrv_stream_opener( php_stream_wrapper* wrapper, __in const char*, __in const char* mode,
int options, __in char **, php_stream_context* STREAMS_DC TSRMLS_DC )
#else
static php_stream* sqlsrv_stream_opener( php_stream_wrapper* wrapper, __in char*, __in char* mode,
int options, __in char **, php_stream_context* STREAMS_DC TSRMLS_DC )
#endif
static php_stream* sqlsrv_stream_opener( php_stream_wrapper* wrapper, _In_ const char*, _In_ const char* mode,
int options, _In_ zend_string **, php_stream_context* STREAMS_DC TSRMLS_DC )
{
#if ZEND_DEBUG

View file

@ -5,7 +5,7 @@
//
// Comments: Mostly error handling and some type handling
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -33,9 +33,9 @@ SQLCHAR INTERNAL_FORMAT_ERROR[] = "An internal error occurred. FormatMessage fa
char last_err_msg[ 2048 ]; // 2k to hold the error messages
// routine used by utf16_string_from_mbcs_string
unsigned int convert_string_from_default_encoding( unsigned int php_encoding, __in_bcount(mbcs_len) char const* mbcs_in_string,
unsigned int convert_string_from_default_encoding( unsigned int php_encoding, _In_reads_bytes_(mbcs_len) char const* mbcs_in_string,
unsigned int mbcs_len,
__out_ecount(utf16_len) __transfer( mbcs_in_string ) wchar_t* utf16_out_string,
_Out_writes_(utf16_len) __transfer( mbcs_in_string ) wchar_t* utf16_out_string,
unsigned int utf16_len );
}
@ -70,18 +70,19 @@ void core_sqlsrv_register_logger( log_callback driver_logger )
// utf-16 string is released by this function if no errors occurred. Otherwise the parameters are not changed
// and false is returned.
bool convert_string_from_utf16_inplace( SQLSRV_ENCODING encoding, char** string, SQLINTEGER& len)
bool convert_string_from_utf16_inplace( SQLSRV_ENCODING encoding, char** string, SQLLEN& len)
{
SQLSRV_ASSERT( string != NULL && *string != NULL, "String must be specified" );
SQLSRV_ASSERT( string != NULL, "String must be specified" );
// for the empty string, we simply returned we converted it
if( len == 0 && *string[0] == '\0' ) {
return true;
}
if (validate_string(*string, len)) {
return true;
}
char* outString = NULL;
SQLINTEGER outLen = 0;
bool result = convert_string_from_utf16( encoding, reinterpret_cast<const wchar_t*>(*string), len / sizeof(wchar_t), &outString, outLen);
SQLLEN outLen = 0;
bool result = convert_string_from_utf16( encoding,
reinterpret_cast<const wchar_t*>(*string), int(len / sizeof(wchar_t)), &outString, outLen);
if (result)
{
@ -93,7 +94,49 @@ bool convert_string_from_utf16_inplace( SQLSRV_ENCODING encoding, char** string,
return result;
}
bool convert_string_from_utf16( SQLSRV_ENCODING encoding, const wchar_t* inString, SQLINTEGER cchInLen, char** outString, SQLINTEGER& cchOutLen )
bool convert_zval_string_from_utf16(SQLSRV_ENCODING encoding, zval* value_z, SQLLEN& len)
{
char* string = Z_STRVAL_P(value_z);
if (validate_string(string, len)) {
return true;
}
char* outString = NULL;
SQLLEN outLen = 0;
bool result = convert_string_from_utf16(encoding,
reinterpret_cast<const wchar_t*>(string), int(len / sizeof(wchar_t)), &outString, outLen);
if (result)
{
core::sqlsrv_zval_stringl(value_z, outString, outLen);
sqlsrv_free(outString);
len = outLen;
}
return result;
}
bool validate_string(char* string, SQLLEN& len)
{
SQLSRV_ASSERT(string != NULL, "String must be specified");
//for the empty string, we simply returned we converted it
if (len == 0 && string[0] == '\0') {
return true;
}
if ((len / sizeof(wchar_t)) > INT_MAX)
{
LOG(SEV_ERROR, "UTP-16 (wide character) string mapping: buffer length exceeded.");
throw core::CoreException();
}
return false;
}
bool convert_string_from_utf16( SQLSRV_ENCODING encoding, const wchar_t* inString, SQLINTEGER cchInLen, char** outString, SQLLEN& cchOutLen )
{
SQLSRV_ASSERT( inString != NULL, "Input string must be specified" );
SQLSRV_ASSERT( outString != NULL, "Output buffer pointer must be specified" );
@ -126,7 +169,7 @@ bool convert_string_from_utf16( SQLSRV_ENCODING encoding, const wchar_t* inStrin
char* newString = reinterpret_cast<char*>( sqlsrv_malloc( cchOutLen + 1 /* NULL char*/ ));
int rc = WideCharToMultiByte( encoding, flags,
inString, cchInLen,
newString, cchOutLen, NULL, NULL );
newString, static_cast<int>(cchOutLen), NULL, NULL );
if( rc == 0 ) {
cchOutLen = 0;
sqlsrv_free( newString );
@ -139,7 +182,6 @@ bool convert_string_from_utf16( SQLSRV_ENCODING encoding, const wchar_t* inStrin
return true;
}
// thin wrapper around convert_string_from_default_encoding that handles
// allocation of the destination string. An empty string passed in returns
// failure since it's a failure case for convert_string_from_default_encoding.
@ -219,10 +261,10 @@ bool core_sqlsrv_get_odbc_error( sqlsrv_context& ctx, int record_number, sqlsrv_
return false;
}
SQLINTEGER sqlstate_len = 0;
SQLLEN sqlstate_len = 0;
convert_string_from_utf16(enc, wsqlstate, sizeof(wsqlstate), (char**)&error->sqlstate, sqlstate_len);
SQLINTEGER message_len = 0;
SQLLEN message_len = 0;
convert_string_from_utf16(enc, wnative_message, wmessage_len, (char**)&error->native_message, message_len);
break;
}
@ -264,12 +306,12 @@ void core_sqlsrv_format_driver_error( sqlsrv_context& ctx, sqlsrv_error_const co
LOG( severity, "%1!s!: message = %2!s!", ctx.func(), formatted_error->native_message );
}
DWORD core_sqlsrv_format_message( char* output_buffer, unsigned output_len, const char* format, ... )
DWORD core_sqlsrv_format_message( char*& output_buffer, unsigned output_len, const char* format, ... )
{
va_list format_args;
va_start( format_args, format );
DWORD rc = FormatMessage( FORMAT_MESSAGE_FROM_STRING, format, 0, 0, output_buffer, output_len, &format_args );
DWORD rc = FormatMessage( FORMAT_MESSAGE_FROM_STRING, format, 0, 0, static_cast<LPSTR>(output_buffer), SQL_MAX_MESSAGE_LENGTH, &format_args );
va_end( format_args );
@ -328,8 +370,8 @@ namespace {
// returned in utf16_out_string. An empty string passed in will result as
// a failure since MBTWC returns 0 for both an empty string and failure
// to convert.
unsigned int convert_string_from_default_encoding( unsigned int php_encoding, __in_bcount(mbcs_len) char const* mbcs_in_string,
unsigned int mbcs_len, __out_ecount(utf16_len) __transfer( mbcs_in_string ) wchar_t* utf16_out_string,
unsigned int convert_string_from_default_encoding( unsigned int php_encoding, _In_reads_bytes_(mbcs_len) char const* mbcs_in_string,
unsigned int mbcs_len, _Out_writes_(utf16_len) __transfer( mbcs_in_string ) wchar_t* utf16_out_string,
unsigned int utf16_len )
{
unsigned int win_encoding = CP_ACP;

View file

@ -888,7 +888,7 @@ 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_setbulkmode (HDBC, INT, _In_reads_bytes_(cbField) void*, INT cbField, _In_reads_bytes_(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);
@ -958,7 +958,7 @@ HANDLE __stdcall OpenSqlFilestream (
LPCWSTR FilestreamPath,
SQL_FILESTREAM_DESIRED_ACCESS DesiredAccess,
ULONG OpenOptions,
__in_bcount(FilestreamTransactionContextLength)
_In_reads_bytes_(FilestreamTransactionContextLength)
LPBYTE FilestreamTransactionContext,
SSIZE_T FilestreamTransactionContextLength,
PLARGE_INTEGER AllocationSize);
@ -995,11 +995,11 @@ extern "C" {
// 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,
_In_z_ PCWSTR wszVersion,
// I the instance name
__in_z PCWSTR pInstanceName,
_In_z_ PCWSTR pInstanceName,
// I reserved for the future use. Currently should be set to 0.
__in DWORD dwFlags
_In_ DWORD dwFlags
);
// type definition for pointer to LocalDBCreateInstance function
@ -1008,14 +1008,14 @@ typedef FnLocalDBCreateInstance* PFnLocalDBCreateInstance;
// type definition for LocalDBStartInstance function
typedef HRESULT __cdecl FnLocalDBStartInstance (
// I the LocalDB instance name
__in_z PCWSTR pInstanceName,
_In_z_ PCWSTR pInstanceName,
// I reserved for the future use. Currently should be set to 0.
__in DWORD dwFlags,
_In_ DWORD dwFlags,
// O the buffer to store the connection string to the LocalDB instance
__out_ecount_z_opt(*lpcchSqlConnection) LPWSTR wszSqlConnection,
_Out_writes_opt_z_(*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
_Inout_opt_ LPDWORD lpcchSqlConnection
);
// type definition for pointer to LocalDBStartInstance function
@ -1027,19 +1027,19 @@ typedef FnLocalDBStartInstance* PFnLocalDBStartInstance;
// type definition for LocalDBFormatMessage function
typedef HRESULT __cdecl FnLocalDBFormatMessage(
// I the LocalDB error code
__in HRESULT hrLocalDB,
_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,
_In_ DWORD dwFlags,
// I Language desired (LCID) or 0 (in which case Win32 FormatMessage order is used)
__in DWORD dwLanguageId,
_In_ DWORD dwLanguageId,
// O the buffer to store the LocalDB error message
__out_ecount_z(*lpcchMessage) LPWSTR wszMessage,
_Out_writes_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
_Inout_ LPDWORD lpcchMessage
);
// type definition for function pointer to LocalDBFormatMessage function
@ -1112,14 +1112,14 @@ FnLocalDBStartInstance LocalDBStartInstance;
// type definition for LocalDBStopInstance function
typedef HRESULT __cdecl FnLocalDBStopInstance (
// I the LocalDB instance name
__in_z PCWSTR pInstanceName,
_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,
_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
_In_ ULONG ulTimeout
);
// type definition for pointer to LocalDBStopInstance function
@ -1149,9 +1149,9 @@ FnLocalDBStopInstance LocalDBStopInstance;
// type definition for LocalDBDeleteInstance function
typedef HRESULT __cdecl FnLocalDBDeleteInstance (
// I the LocalDB instance name
__in_z PCWSTR pInstanceName,
_In_z_ PCWSTR pInstanceName,
// I reserved for the future use. Currently should be set to 0.
__in DWORD dwFlags
_In_ DWORD dwFlags
);
// type definition for pointer to LocalDBDeleteInstance function
@ -1202,10 +1202,10 @@ typedef TLocalDBInstanceName* PTLocalDBInstanceName;
// type definition for LocalDBGetInstances function
typedef HRESULT __cdecl FnLocalDBGetInstances(
// O buffer for a LocalDB instance names
__out PTLocalDBInstanceName pInstanceNames,
_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
_Inout_ LPDWORD lpdwNumberOfInstances
);
// type definition for pointer to LocalDBGetInstances function
@ -1267,11 +1267,11 @@ typedef LocalDBInstanceInfo* PLocalDBInstanceInfo;
// type definition for LocalDBGetInstanceInfo function
typedef HRESULT __cdecl FnLocalDBGetInstanceInfo(
// I the LocalDB instance name
__in_z PCWSTR wszInstanceName,
_In_z_ PCWSTR wszInstanceName,
// O instance information
__out PLocalDBInstanceInfo pInfo,
_Out_ PLocalDBInstanceInfo pInfo,
// I Size of LocalDBInstanceInfo structure in bytes
__in DWORD cbInfo);
_In_ DWORD cbInfo);
// type definition for pointer to LocalDBGetInstances function
typedef FnLocalDBGetInstanceInfo* PFnLocalDBGetInstanceInfo;
@ -1298,10 +1298,10 @@ typedef TLocalDBVersion* PTLocalDBVersion;
// type definition for LocalDBGetVersions function
typedef HRESULT __cdecl FnLocalDBGetVersions(
// O buffer for installed LocalDB versions
__out PTLocalDBVersion pVersions,
_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
_Inout_ LPDWORD lpdwNumberOfVersions
);
// type definition for pointer to LocalDBGetVersions function
@ -1352,11 +1352,11 @@ typedef LocalDBVersionInfo* PLocalDBVersionInfo;
// type definition for LocalDBGetVersionInfo function
typedef HRESULT __cdecl FnLocalDBGetVersionInfo(
// I LocalDB version string
__in_z PCWSTR wszVersion,
_In_z_ PCWSTR wszVersion,
// O version information
__out PLocalDBVersionInfo pVersionInfo,
_Out_ PLocalDBVersionInfo pVersionInfo,
// I Size of LocalDBVersionInfo structure in bytes
__in DWORD cbVersionInfo
_In_ DWORD cbVersionInfo
);
// type definition for pointer to LocalDBGetVersionInfo function
@ -1402,13 +1402,13 @@ FnLocalDBStopTracing LocalDBStopTracing;
// type definition for LocalDBShareInstance function
typedef HRESULT __cdecl FnLocalDBShareInstance(
// I the SID of the LocalDB instance owner
__in_opt PSID pOwnerSID,
_In_opt_ PSID pOwnerSID,
// I the private name of LocalDB instance which should be shared
__in_z PCWSTR wszPrivateLocalDBInstanceName,
_In_z_ PCWSTR wszPrivateLocalDBInstanceName,
// I the public shared name
__in_z PCWSTR wszSharedName,
_In_z_ PCWSTR wszSharedName,
// I reserved for the future use. Currently should be set to 0.
__in DWORD dwFlags);
_In_ DWORD dwFlags);
// type definition for pointer to LocalDBShareInstance function
typedef FnLocalDBShareInstance* PFnLocalDBShareInstance;
@ -1426,9 +1426,9 @@ FnLocalDBShareInstance LocalDBShareInstance;
// type definition for LocalDBUnshareInstance function
typedef HRESULT __cdecl FnLocalDBUnshareInstance(
// I the LocalDB instance name
__in_z PCWSTR pInstanceName,
_In_z_ PCWSTR pInstanceName,
// I reserved for the future use. Currently should be set to 0.
__in DWORD dwFlags);
_In_ DWORD dwFlags);
// type definition for pointer to LocalDBUnshareInstance function
typedef FnLocalDBUnshareInstance* PFnLocalDBUnshareInstance;
@ -1653,11 +1653,11 @@ Cleanup:
HRESULT __cdecl
LocalDBCreateInstance (
// I the LocalDB version (e.g. 11.0 or 11.0.1094.2)
__in_z PCWSTR wszVersion,
_In_z_ PCWSTR wszVersion,
// I the instance name
__in_z PCWSTR pInstanceName,
_In_z_ PCWSTR pInstanceName,
// I reserved for the future use. Currently should be set to 0.
__in DWORD dwFlags
_In_ DWORD dwFlags
)
{
LOCALDB_PROXY(LocalDBCreateInstance)(wszVersion, pInstanceName, dwFlags);
@ -1666,14 +1666,14 @@ LocalDBCreateInstance (
HRESULT __cdecl
LocalDBStartInstance(
// I the instance name
__in_z PCWSTR pInstanceName,
_In_z_ PCWSTR pInstanceName,
// I reserved for the future use. Currently should be set to 0.
__in DWORD dwFlags,
_In_ DWORD dwFlags,
// O the buffer to store the connection string to the LocalDB instance
__out_ecount_z_opt(*lpcchSqlConnection) LPWSTR wszSqlConnection,
_Out_writes_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
_Inout_opt_ LPDWORD lpcchSqlConnection
)
{
LOCALDB_PROXY(LocalDBStartInstance)(pInstanceName, dwFlags, wszSqlConnection, lpcchSqlConnection);
@ -1682,14 +1682,14 @@ LocalDBStartInstance(
HRESULT __cdecl
LocalDBStopInstance (
// I the instance name
__in_z PCWSTR pInstanceName,
_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,
_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
_In_ ULONG ulTimeout
)
{
LOCALDB_PROXY(LocalDBStopInstance)(pInstanceName, dwFlags, ulTimeout);
@ -1698,9 +1698,9 @@ LocalDBStopInstance (
HRESULT __cdecl
LocalDBDeleteInstance (
// I the instance name
__in_z PCWSTR pInstanceName,
_In_z_ PCWSTR pInstanceName,
// reserved for the future use. Currently should be set to 0.
__in DWORD dwFlags
_In_ DWORD dwFlags
)
{
LOCALDB_PROXY(LocalDBDeleteInstance)(pInstanceName, dwFlags);
@ -1709,19 +1709,19 @@ LocalDBDeleteInstance (
HRESULT __cdecl
LocalDBFormatMessage(
// I the LocalDB error code
__in HRESULT hrLocalDB,
_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,
_In_ DWORD dwFlags,
// I Language desired (LCID) or 0 (in which case Win32 FormatMessage order is used)
__in DWORD dwLanguageId,
_In_ DWORD dwLanguageId,
// O the buffer to store the LocalDB error message
__out_ecount_z(*lpcchMessage) LPWSTR wszMessage,
_Out_writes_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
_Inout_ LPDWORD lpcchMessage
)
{
LOCALDB_PROXY(LocalDBFormatMessage)(hrLocalDB, dwFlags, dwLanguageId, wszMessage, lpcchMessage);
@ -1730,10 +1730,10 @@ LocalDBFormatMessage(
HRESULT __cdecl
LocalDBGetInstances(
// O buffer with instance names
__out PTLocalDBInstanceName pInstanceNames,
_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
_Inout_ LPDWORD lpdwNumberOfInstances
)
{
LOCALDB_PROXY(LocalDBGetInstances)(pInstanceNames, lpdwNumberOfInstances);
@ -1742,11 +1742,11 @@ LocalDBGetInstances(
HRESULT __cdecl
LocalDBGetInstanceInfo(
// I the instance name
__in_z PCWSTR wszInstanceName,
_In_z_ PCWSTR wszInstanceName,
// O instance information
__out PLocalDBInstanceInfo pInfo,
_Out_ PLocalDBInstanceInfo pInfo,
// I Size of LocalDBInstanceInfo structure in bytes
__in DWORD cbInfo
_In_ DWORD cbInfo
)
{
LOCALDB_PROXY(LocalDBGetInstanceInfo)(wszInstanceName, pInfo, cbInfo);
@ -1767,13 +1767,13 @@ LocalDBStopTracing()
HRESULT __cdecl
LocalDBShareInstance(
// I the SID of the LocalDB instance owner
__in_opt PSID pOwnerSID,
_In_opt_ PSID pOwnerSID,
// I the private name of LocalDB instance which should be shared
__in_z PCWSTR wszLocalDBInstancePrivateName,
_In_z_ PCWSTR wszLocalDBInstancePrivateName,
// I the public shared name
__in_z PCWSTR wszSharedName,
_In_z_ PCWSTR wszSharedName,
// I reserved for the future use. Currently should be set to 0.
__in DWORD dwFlags)
_In_ DWORD dwFlags)
{
LOCALDB_PROXY(LocalDBShareInstance)(pOwnerSID, wszLocalDBInstancePrivateName, wszSharedName, dwFlags);
}
@ -1781,10 +1781,10 @@ LocalDBShareInstance(
HRESULT __cdecl
LocalDBGetVersions(
// O buffer for installed LocalDB versions
__out PTLocalDBVersion pVersions,
_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
_Inout_ LPDWORD lpdwNumberOfVersions
)
{
LOCALDB_PROXY(LocalDBGetVersions)(pVersions, lpdwNumberOfVersions);
@ -1793,9 +1793,9 @@ LocalDBGetVersions(
HRESULT __cdecl
LocalDBUnshareInstance(
// I the LocalDB instance name
__in_z PCWSTR pInstanceName,
_In_z_ PCWSTR pInstanceName,
// I reserved for the future use. Currently should be set to 0.
__in DWORD dwFlags)
_In_ DWORD dwFlags)
{
LOCALDB_PROXY(LocalDBUnshareInstance)(pInstanceName, dwFlags);
}
@ -1803,11 +1803,11 @@ LocalDBUnshareInstance(
HRESULT __cdecl
LocalDBGetVersionInfo(
// I LocalDB version string
__in_z PCWSTR wszVersion,
_In_z_ PCWSTR wszVersion,
// O version information
__out PLocalDBVersionInfo pVersionInfo,
_Out_ PLocalDBVersionInfo pVersionInfo,
// I Size of LocalDBVersionInfo structure in bytes
__in DWORD cbVersionInfo)
_In_ DWORD cbVersionInfo)
{
LOCALDB_PROXY(LocalDBGetVersionInfo)(wszVersion, pVersionInfo, cbVersionInfo);
}

View file

@ -3,7 +3,7 @@
//
// Contents: Implements the PDO object for PDO_SQLSRV
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -33,7 +33,7 @@ typedef const zend_function_entry pdo_sqlsrv_function_entry;
namespace {
const char LAST_INSERT_ID_QUERY[] = "SELECT @@IDENTITY;";
const char LAST_INSERT_ID_BUFF_LEN = 10; // size of the buffer to hold the string value of the last insert id integer
const size_t LAST_INSERT_ID_BUFF_LEN = 10; // size of the buffer to hold the string value of the last insert id integer
const char TABLE_LAST_INSERT_ID_QUERY[] = "SELECT IDENT_CURRENT(%s)";
const int LAST_INSERT_ID_QUERY_MAX_LEN = sizeof( TABLE_LAST_INSERT_ID_QUERY ) + SQL_MAX_SQLSERVERNAME + 2; // include the quotes
@ -73,20 +73,22 @@ enum PDO_STMT_OPTIONS {
PDO_STMT_OPTION_CURSOR_SCROLL_TYPE,
PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE,
PDO_STMT_OPTION_EMULATE_PREPARES,
PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE,
};
// List of all the statement options supported by this driver.
const stmt_option PDO_STMT_OPTS[] = {
{ NULL, 0, SQLSRV_STMT_OPTION_QUERY_TIMEOUT, new stmt_option_query_timeout },
{ NULL, 0, SQLSRV_STMT_OPTION_SCROLLABLE, new stmt_option_scrollable },
{ NULL, 0, PDO_STMT_OPTION_ENCODING, new stmt_option_encoding },
{ NULL, 0, PDO_STMT_OPTION_DIRECT_QUERY, new stmt_option_direct_query },
{ NULL, 0, PDO_STMT_OPTION_CURSOR_SCROLL_TYPE, new stmt_option_cursor_scroll_type },
{ NULL, 0, PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE, new stmt_option_buffered_query_limit },
{ NULL, 0, PDO_STMT_OPTION_EMULATE_PREPARES, new stmt_option_emulate_prepares },
{ NULL, 0, SQLSRV_STMT_OPTION_QUERY_TIMEOUT, std::unique_ptr<stmt_option_query_timeout>( new stmt_option_query_timeout ) },
{ NULL, 0, SQLSRV_STMT_OPTION_SCROLLABLE, std::unique_ptr<stmt_option_scrollable>( new stmt_option_scrollable ) },
{ NULL, 0, PDO_STMT_OPTION_ENCODING, std::unique_ptr<stmt_option_encoding>( new stmt_option_encoding ) },
{ NULL, 0, PDO_STMT_OPTION_DIRECT_QUERY, std::unique_ptr<stmt_option_direct_query>( new stmt_option_direct_query ) },
{ NULL, 0, PDO_STMT_OPTION_CURSOR_SCROLL_TYPE, std::unique_ptr<stmt_option_cursor_scroll_type>( new stmt_option_cursor_scroll_type ) },
{ NULL, 0, PDO_STMT_OPTION_CLIENT_BUFFER_MAX_KB_SIZE, std::unique_ptr<stmt_option_buffered_query_limit>( new stmt_option_buffered_query_limit ) },
{ NULL, 0, PDO_STMT_OPTION_EMULATE_PREPARES, std::unique_ptr<stmt_option_emulate_prepares>( new stmt_option_emulate_prepares ) },
{ NULL, 0, PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE, std::unique_ptr<stmt_option_fetch_numeric>( new stmt_option_fetch_numeric ) },
{ NULL, 0, SQLSRV_STMT_OPTION_INVALID, NULL},
{ NULL, 0, SQLSRV_STMT_OPTION_INVALID, std::unique_ptr<stmt_option_functor>{} },
};
// boolean connection string
@ -109,7 +111,7 @@ struct pdo_int_conn_attr_func {
SQLSRV_ASSERT( Z_TYPE_P( value ) == IS_STRING, "pdo_int_conn_attr_func: Unexpected zval type." );
int val = atoi( Z_STRVAL_P( value ));
size_t val = static_cast<size_t>( atoi( Z_STRVAL_P( value )) );
core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( val ), SQL_IS_UINTEGER TSRMLS_CC );
}
catch( core::CoreException& ) {
@ -135,9 +137,9 @@ struct pdo_bool_conn_attr_func {
};
// statement options related functions
void add_stmt_option_key( sqlsrv_context& ctx, unsigned long key, HashTable* options_ht,
void add_stmt_option_key( sqlsrv_context& ctx, size_t key, HashTable* options_ht,
zval** data TSRMLS_DC );
void validate_stmt_options( sqlsrv_context& ctx, zval* stmt_options, __inout HashTable* pdo_stmt_options_ht TSRMLS_DC );
void validate_stmt_options( sqlsrv_context& ctx, zval* stmt_options, _Inout_ HashTable* pdo_stmt_options_ht TSRMLS_DC );
} // namespace
@ -306,8 +308,8 @@ int pdo_sqlsrv_dbh_close( pdo_dbh_t *dbh TSRMLS_DC );
// execute queries
int pdo_sqlsrv_dbh_prepare( pdo_dbh_t *dbh, const char *sql,
long sql_len, pdo_stmt_t *stmt, zval *driver_options TSRMLS_DC );
long pdo_sqlsrv_dbh_do( pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC );
size_t sql_len, pdo_stmt_t *stmt, zval *driver_options TSRMLS_DC );
zend_long pdo_sqlsrv_dbh_do( pdo_dbh_t *dbh, const char *sql, size_t sql_len TSRMLS_DC );
// transaction support functions
int pdo_sqlsrv_dbh_commit( pdo_dbh_t *dbh TSRMLS_DC );
@ -315,21 +317,21 @@ int pdo_sqlsrv_dbh_begin( pdo_dbh_t *dbh TSRMLS_DC );
int pdo_sqlsrv_dbh_rollback( pdo_dbh_t *dbh TSRMLS_DC );
// attribute functions
int pdo_sqlsrv_dbh_set_attr( pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC );
int pdo_sqlsrv_dbh_get_attr( pdo_dbh_t *dbh, long attr, zval *return_value TSRMLS_DC );
int pdo_sqlsrv_dbh_set_attr( pdo_dbh_t *dbh, zend_long attr, zval *val TSRMLS_DC );
int pdo_sqlsrv_dbh_get_attr( pdo_dbh_t *dbh, zend_long attr, zval *return_value TSRMLS_DC );
// return more information
int pdo_sqlsrv_dbh_return_error( pdo_dbh_t *dbh, pdo_stmt_t *stmt,
zval *info TSRMLS_DC);
// return the last id generated by an executed SQL statement
char * pdo_sqlsrv_dbh_last_id( pdo_dbh_t *dbh, const char *name, unsigned int* len TSRMLS_DC );
char * pdo_sqlsrv_dbh_last_id( pdo_dbh_t *dbh, const char *name, size_t* len TSRMLS_DC );
// additional methods are supported in this function
pdo_sqlsrv_function_entry *pdo_sqlsrv_get_driver_methods( pdo_dbh_t *dbh, int kind TSRMLS_DC );
// quote a string, meaning put quotes around it and escape any quotes within it
int pdo_sqlsrv_dbh_quote( pdo_dbh_t* dbh, const char* unquoted, int unquotedlen, char **quoted, int* quotedlen,
int pdo_sqlsrv_dbh_quote( pdo_dbh_t* dbh, const char* unquoted, size_t unquotedlen, char **quoted, size_t* quotedlen,
enum pdo_param_type paramtype TSRMLS_DC );
struct pdo_dbh_methods pdo_sqlsrv_dbh_methods = {
@ -347,7 +349,8 @@ struct pdo_dbh_methods pdo_sqlsrv_dbh_methods = {
pdo_sqlsrv_dbh_get_attr,
NULL, // check liveness not implemented
pdo_sqlsrv_get_driver_methods,
NULL // request shutdown not implemented
NULL, // request shutdown not implemented
NULL // in transaction not implemented
};
@ -361,11 +364,13 @@ struct pdo_dbh_methods pdo_sqlsrv_dbh_methods = {
// constructor for the internal object for connections
pdo_sqlsrv_dbh::pdo_sqlsrv_dbh( SQLHANDLE h, error_callback e, void* driver TSRMLS_DC ) :
sqlsrv_conn( h, e, driver, SQLSRV_ENCODING_UTF8 TSRMLS_CC ),
stmts( NULL ),
direct_query( false ),
query_timeout( QUERY_TIMEOUT_INVALID ),
client_buffer_max_size( PDO_SQLSRV_G( client_buffer_max_size ) )
sqlsrv_conn( h, e, driver, SQLSRV_ENCODING_UTF8 TSRMLS_CC ),
stmts( NULL ),
direct_query( false ),
query_timeout( QUERY_TIMEOUT_INVALID ),
client_buffer_max_size( PDO_SQLSRV_G( client_buffer_max_size )),
bind_param_encoding( SQLSRV_ENCODING_CHAR ),
fetch_numeric( false )
{
if( client_buffer_max_size < 0 ) {
client_buffer_max_size = sqlsrv_buffered_result_set::BUFFERED_QUERY_LIMIT_DEFAULT;
@ -400,9 +405,10 @@ int pdo_sqlsrv_db_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC)
// object for errors.
dbh->methods = &pdo_sqlsrv_dbh_methods;
dbh->driver_data = NULL;
zval** temp_server_z = NULL;
zval* temp_server_z = NULL;
sqlsrv_malloc_auto_ptr<conn_string_parser> dsn_parser;
zval_auto_ptr server_z;
zval server_z;
ZVAL_UNDEF( &server_z );
try {
@ -420,16 +426,16 @@ int pdo_sqlsrv_db_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC)
// Initialize the options array to be passed to the core layer
ALLOC_HASHTABLE( pdo_conn_options_ht );
core::sqlsrv_zend_hash_init( *g_henv_cp, pdo_conn_options_ht, 10 /* # of buckets */, NULL /*hashfn*/,
ZVAL_PTR_DTOR, 0 /*persistent*/ TSRMLS_CC );
core::sqlsrv_zend_hash_init( *g_henv_cp, pdo_conn_options_ht, 10 /* # of buckets */,
ZVAL_INTERNAL_DTOR, 0 /*persistent*/ TSRMLS_CC );
// Either of g_henv_cp or g_henv_ncp can be used to propogate the error.
dsn_parser = new ( sqlsrv_malloc( sizeof( conn_string_parser ))) conn_string_parser( *g_henv_cp, dbh->data_source,
dbh->data_source_len, pdo_conn_options_ht );
static_cast<int>( dbh->data_source_len ), pdo_conn_options_ht );
dsn_parser->parse_conn_string( TSRMLS_C );
// Extract the server name
zend_hash_index_find( pdo_conn_options_ht, PDO_CONN_OPTION_SERVER, (void**)&temp_server_z );
temp_server_z = zend_hash_index_find( pdo_conn_options_ht, PDO_CONN_OPTION_SERVER);
CHECK_CUSTOM_ERROR(( temp_server_z == NULL ), g_henv_cp, PDO_SQLSRV_ERROR_SERVER_NOT_SPECIFIED ) {
@ -442,9 +448,12 @@ int pdo_sqlsrv_db_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC)
zval_add_ref( &server_z );
zend_hash_index_del( pdo_conn_options_ht, PDO_CONN_OPTION_SERVER );
sqlsrv_conn* conn = core_sqlsrv_connect( *g_henv_cp, *g_henv_ncp, core::allocate_conn<pdo_sqlsrv_dbh>, Z_STRVAL_P( server_z ),
sqlsrv_conn* conn = core_sqlsrv_connect( *g_henv_cp, *g_henv_ncp, core::allocate_conn<pdo_sqlsrv_dbh>, Z_STRVAL( server_z ),
dbh->username, dbh->password, pdo_conn_options_ht, pdo_sqlsrv_handle_dbh_error,
PDO_CONN_OPTS, dbh, "pdo_sqlsrv_db_handle_factory" TSRMLS_CC );
// Free the string in server_z after being used
zend_string_release( Z_STR( server_z ));
SQLSRV_ASSERT( conn != NULL, "Invalid connection returned. Exception should have been thrown." );
@ -456,16 +465,18 @@ int pdo_sqlsrv_db_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRMLS_DC)
}
catch( core::CoreException& ) {
if ( Z_TYPE( server_z ) == IS_STRING ) {
zend_string_release( Z_STR( server_z ));
}
dbh->error_mode = prev_err_mode; // reset the error mode
g_henv_cp->last_error().reset(); // reset the last error; callee will check if last_error exist before freeing it and setting it to NULL
return 0;
}
catch( ... ) {
DIE( "pdo_sqlsrv_db_handle_factory: Unknown exception caught" );
}
return 1;
}
@ -510,7 +521,7 @@ int pdo_sqlsrv_dbh_close( pdo_dbh_t *dbh TSRMLS_DC )
// Return:
// 0 for failure, 1 for success.
int pdo_sqlsrv_dbh_prepare( pdo_dbh_t *dbh, const char *sql,
long sql_len, pdo_stmt_t *stmt, zval *driver_options TSRMLS_DC )
size_t sql_len, pdo_stmt_t *stmt, zval *driver_options TSRMLS_DC )
{
PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN;
@ -518,7 +529,7 @@ int pdo_sqlsrv_dbh_prepare( pdo_dbh_t *dbh, const char *sql,
hash_auto_ptr pdo_stmt_options_ht;
sqlsrv_malloc_auto_ptr<char> sql_rewrite;
int sql_rewrite_len = 0;
size_t sql_rewrite_len = 0;
sqlsrv_malloc_auto_ptr<pdo_sqlsrv_stmt> driver_stmt;
pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast<pdo_sqlsrv_dbh*>( dbh->driver_data );
@ -533,7 +544,7 @@ int pdo_sqlsrv_dbh_prepare( pdo_dbh_t *dbh, const char *sql,
// Initialize the options array to be passed to the core layer
ALLOC_HASHTABLE( pdo_stmt_options_ht );
core::sqlsrv_zend_hash_init( *driver_dbh , pdo_stmt_options_ht, 3 /* # of buckets */, NULL /*hashfn*/,
core::sqlsrv_zend_hash_init( *driver_dbh , pdo_stmt_options_ht, 3 /* # of buckets */,
ZVAL_PTR_DTOR, 0 /*persistent*/ TSRMLS_CC );
// Either of g_henv_cp or g_henv_ncp can be used to propogate the error.
@ -635,7 +646,7 @@ int pdo_sqlsrv_dbh_prepare( pdo_dbh_t *dbh, const char *sql,
// sql_len - length of sql query
// Return
// # of rows affected, -1 for an error.
long pdo_sqlsrv_dbh_do( pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC )
zend_long pdo_sqlsrv_dbh_do( pdo_dbh_t *dbh, const char *sql, size_t sql_len TSRMLS_DC )
{
PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN;
@ -644,7 +655,7 @@ long pdo_sqlsrv_dbh_do( pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC
pdo_sqlsrv_dbh* driver_dbh = static_cast<pdo_sqlsrv_dbh*>( dbh->driver_data );
sqlsrv_malloc_auto_ptr<sqlsrv_stmt> driver_stmt;
long rows = 0;
SQLLEN rows = 0;
// verify that the data type sizes are the same. If we ever upgrade to 64 bit we don't want the wrong
// thing to happen here.
@ -662,7 +673,7 @@ long pdo_sqlsrv_dbh_do( pdo_dbh_t *dbh, const char *sql, long sql_len TSRMLS_DC
NULL /*valid_stmt_opts*/, pdo_sqlsrv_handle_stmt_error, &temp_stmt TSRMLS_CC );
driver_stmt->set_func( __FUNCTION__ );
core_sqlsrv_execute( driver_stmt TSRMLS_CC, sql, sql_len );
core_sqlsrv_execute( driver_stmt TSRMLS_CC, sql, static_cast<int>( sql_len ) );
// since the user can give us a compound statement, we return the row count for the last set, and since the row count
// isn't guaranteed to be valid until all the results have been fetched, we fetch them all first.
@ -843,7 +854,7 @@ int pdo_sqlsrv_dbh_rollback( pdo_dbh_t *dbh TSRMLS_DC )
// val - The value of the attribute to be set.
// Return:
// 0 for failure, 1 for success.
int pdo_sqlsrv_dbh_set_attr( pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC )
int pdo_sqlsrv_dbh_set_attr( pdo_dbh_t *dbh, zend_long attr, zval *val TSRMLS_DC )
{
PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN;
@ -857,7 +868,7 @@ int pdo_sqlsrv_dbh_set_attr( pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC )
case SQLSRV_ATTR_ENCODING:
{
long attr_value;
zend_long attr_value;
if( Z_TYPE_P( val ) != IS_LONG ) {
THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_ENCODING );
}
@ -888,7 +899,7 @@ int pdo_sqlsrv_dbh_set_attr( pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC )
convert_to_string( val );
THROW_PDO_ERROR( driver_dbh, SQLSRV_ERROR_INVALID_QUERY_TIMEOUT_VALUE, Z_STRVAL_P( val ));
}
driver_dbh->query_timeout = Z_LVAL_P( val );
driver_dbh->query_timeout = static_cast<long>( Z_LVAL_P( val ) );
break;
case SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE:
@ -899,6 +910,10 @@ int pdo_sqlsrv_dbh_set_attr( pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC )
driver_dbh->client_buffer_max_size = Z_LVAL_P( val );
break;
case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE:
driver_dbh->fetch_numeric = (zend_is_true(val)) ? true : false;
break;
// Not supported
case PDO_ATTR_FETCH_TABLE_NAMES:
case PDO_ATTR_FETCH_CATALOG_NAMES:
@ -954,7 +969,7 @@ int pdo_sqlsrv_dbh_set_attr( pdo_dbh_t *dbh, long attr, zval *val TSRMLS_DC )
// return_value - zval in which to return the attribute value.
// Return:
// 0 for failure, 1 for success.
int pdo_sqlsrv_dbh_get_attr( pdo_dbh_t *dbh, long attr, zval *return_value TSRMLS_DC )
int pdo_sqlsrv_dbh_get_attr( pdo_dbh_t *dbh, zend_long attr, zval *return_value TSRMLS_DC )
{
PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN;
@ -1040,6 +1055,12 @@ int pdo_sqlsrv_dbh_get_attr( pdo_dbh_t *dbh, long attr, zval *return_value TSRML
break;
}
case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE:
{
ZVAL_BOOL( return_value, driver_dbh->fetch_numeric );
break;
}
default:
{
THROW_PDO_ERROR( driver_dbh, PDO_SQLSRV_ERROR_INVALID_DBH_ATTR );
@ -1074,8 +1095,8 @@ int pdo_sqlsrv_dbh_return_error( pdo_dbh_t *dbh, pdo_stmt_t *stmt,
else {
ctx_error = static_cast<sqlsrv_conn*>( dbh->driver_data )->last_error();
}
pdo_sqlsrv_retrieve_context_error( ctx_error, info );
pdo_sqlsrv_retrieve_context_error( ctx_error, info );
return 1;
}
@ -1089,7 +1110,7 @@ int pdo_sqlsrv_dbh_return_error( pdo_dbh_t *dbh, pdo_stmt_t *stmt,
// len - Length of the name.
// Return:
// Returns the last insert id as a string.
char * pdo_sqlsrv_dbh_last_id( pdo_dbh_t *dbh, const char *name, __out unsigned int* len TSRMLS_DC )
char * pdo_sqlsrv_dbh_last_id( pdo_dbh_t *dbh, const char *name, _Out_ size_t* len TSRMLS_DC )
{
PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN;
@ -1101,7 +1122,7 @@ char * pdo_sqlsrv_dbh_last_id( pdo_dbh_t *dbh, const char *name, __out unsigned
sqlsrv_malloc_auto_ptr<sqlsrv_stmt> driver_stmt;
pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast<pdo_sqlsrv_dbh*>( dbh->driver_data );
pdo_sqlsrv_dbh* driver_dbh = static_cast<pdo_sqlsrv_dbh*>( dbh->driver_data );
sqlsrv_malloc_auto_ptr<char> id_str;
id_str = reinterpret_cast<char*>( sqlsrv_malloc( LAST_INSERT_ID_BUFF_LEN ));
@ -1114,7 +1135,7 @@ char * pdo_sqlsrv_dbh_last_id( pdo_dbh_t *dbh, const char *name, __out unsigned
}
else {
char* quoted_table = NULL;
int quoted_len = 0;
size_t quoted_len = 0;
int quoted = pdo_sqlsrv_dbh_quote( dbh, name, strlen( name ), &quoted_table, &quoted_len, PDO_PARAM_NULL TSRMLS_CC );
SQLSRV_ASSERT( quoted, "PDO::lastInsertId failed to quote the table name." );
sprintf_s( last_insert_id_query, LAST_INSERT_ID_QUERY_MAX_LEN, TABLE_LAST_INSERT_ID_QUERY, quoted_table );
@ -1134,11 +1155,10 @@ char * pdo_sqlsrv_dbh_last_id( pdo_dbh_t *dbh, const char *name, __out unsigned
core::SQLExecDirect( driver_stmt, last_insert_id_query TSRMLS_CC );
core::SQLFetchScroll( driver_stmt, SQL_FETCH_NEXT, 0 TSRMLS_CC );
SQLSRV_STATIC_ASSERT( sizeof( SQLLEN ) == sizeof( unsigned int ));
SQLRETURN r = core::SQLGetData( driver_stmt, 1, SQL_C_CHAR, id_str, LAST_INSERT_ID_BUFF_LEN,
reinterpret_cast<SQLLEN*>( len ), false TSRMLS_CC );
CHECK_CUSTOM_ERROR( (!SQL_SUCCEEDED( r ) || *len == SQL_NULL_DATA || *len == SQL_NO_TOTAL), driver_stmt,
CHECK_CUSTOM_ERROR( (!SQL_SUCCEEDED( r ) || *len == SQL_NULL_DATA || *len == SQL_NO_TOTAL), driver_stmt,
PDO_SQLSRV_ERROR_LAST_INSERT_ID ) {
throw core::CoreException();
}
@ -1181,44 +1201,84 @@ char * pdo_sqlsrv_dbh_last_id( pdo_dbh_t *dbh, const char *name, __out unsigned
// quoted_len - Length of the output string.
// Return:
// 0 for failure, 1 for success.
int pdo_sqlsrv_dbh_quote( pdo_dbh_t* dbh, const char* unquoted, int unquoted_len, char **quoted, int* quoted_len,
int pdo_sqlsrv_dbh_quote( pdo_dbh_t* dbh, const char* unquoted, size_t unquoted_len, char **quoted, size_t* quoted_len,
enum pdo_param_type /*paramtype*/ TSRMLS_DC )
{
PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN;
PDO_RESET_DBH_ERROR;
PDO_VALIDATE_CONN;
PDO_LOG_DBH_ENTRY;
// count the number of quotes needed
unsigned int quotes_needed = 2; // the initial start and end quotes of course
for( int index = 0; index < unquoted_len && unquoted[ index ] != '\0'; ++index ) {
if( unquoted[ index ] == '\'' ) {
++quotes_needed;
}
}
pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast<pdo_sqlsrv_dbh*>( dbh->driver_data );
SQLSRV_ENCODING encoding = driver_dbh->bind_param_encoding;
*quoted_len = unquoted_len + quotes_needed; // length returned to the caller should not account for null terminator.
*quoted = reinterpret_cast<char*>( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 )); // include space for null terminator.
unsigned int out_current = 0;
if ( encoding == SQLSRV_ENCODING_BINARY ) {
// convert from char* to hex digits using os
std::basic_ostringstream<char> os;
for ( size_t index = 0; index < unquoted_len && unquoted[index] != '\0'; ++index ) {
os << std::hex << ( int )unquoted[index];
}
std::basic_string<char> str_hex = os.str();
// each character is represented by 2 digits of hex
size_t unquoted_str_len = unquoted_len * 2; // length returned should not account for null terminator
char* unquoted_str = reinterpret_cast<char*>( sqlsrv_malloc( unquoted_str_len, sizeof( char ), 1 )); // include space for null terminator
strcpy_s( unquoted_str, unquoted_str_len + 1 /* include null terminator*/, str_hex.c_str());
// include length of '0x' in the binary string
*quoted_len = unquoted_str_len + 2;
*quoted = reinterpret_cast<char*>( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 ));
unsigned int out_current = 0;
// insert '0x'
( *quoted )[out_current++] = '0';
( *quoted )[out_current++] = 'x';
for ( size_t index = 0; index < unquoted_str_len && unquoted_str[index] != '\0'; ++index ) {
( *quoted )[out_current++] = unquoted_str[index];
}
// null terminator
( *quoted )[out_current] = '\0';
sqlsrv_free( unquoted_str );
return 1;
}
else {
// count the number of quotes needed
unsigned int quotes_needed = 2; // the initial start and end quotes of course
// include the N proceeding the initial quote if encoding is UTF8
if ( encoding == SQLSRV_ENCODING_UTF8 ) {
quotes_needed = 3;
}
// insert initial quote
(*quoted)[ out_current++ ] ='\'';
for ( size_t index = 0; index < unquoted_len && unquoted[index] != '\0'; ++index ) {
if ( unquoted[index] == '\'' ) {
++quotes_needed;
}
}
for( int index = 0; index < unquoted_len && unquoted[ index ] != '\0'; ++index ) {
*quoted_len = unquoted_len + quotes_needed; // length returned to the caller should not account for null terminator.
*quoted = reinterpret_cast<char*>( sqlsrv_malloc( *quoted_len, sizeof( char ), 1 )); // include space for null terminator.
unsigned int out_current = 0;
if( unquoted[ index ] == '\'' ) {
(*quoted)[ out_current++ ] = '\'';
(*quoted)[ out_current++ ] = '\'';
}
else {
(*quoted)[ out_current++ ] = unquoted[ index ];
}
}
// insert N if the encoding is UTF8
if ( encoding == SQLSRV_ENCODING_UTF8 ) {
( *quoted )[out_current++] = 'N';
}
// insert initial quote
( *quoted )[out_current++] = '\'';
// trailing quote and null terminator
(*quoted)[ out_current++ ] ='\'';
(*quoted)[ out_current ] = '\0';
for ( size_t index = 0; index < unquoted_len && unquoted[index] != '\0'; ++index ) {
return 1;
if ( unquoted[index] == '\'' ) {
( *quoted )[out_current++] = '\'';
( *quoted )[out_current++] = '\'';
}
else {
( *quoted )[out_current++] = unquoted[index];
}
}
// trailing quote and null terminator
( *quoted )[out_current++] = '\'';
( *quoted )[out_current] = '\0';
return 1;
}
}
// This method is not implemented by this driver.
@ -1240,10 +1300,10 @@ namespace {
// Maps the PDO driver specific statement option/attribute constants to the core layer
// statement option/attribute constants.
void add_stmt_option_key( sqlsrv_context& ctx, unsigned long key, HashTable* options_ht,
zval** data TSRMLS_DC )
void add_stmt_option_key( sqlsrv_context& ctx, size_t key, HashTable* options_ht,
zval* data TSRMLS_DC )
{
unsigned long option_key = -1;
zend_ulong option_key = -1;
switch( key ) {
case PDO_ATTR_CURSOR:
@ -1277,6 +1337,9 @@ void add_stmt_option_key( sqlsrv_context& ctx, unsigned long key, HashTable* opt
option_key = PDO_STMT_OPTION_EMULATE_PREPARES;
break;
case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE:
option_key = PDO_STMT_OPTION_FETCHES_NUMERIC_TYPE;
default:
CHECK_CUSTOM_ERROR( true, ctx, PDO_SQLSRV_ERROR_INVALID_STMT_OPTION ) {
throw core::CoreException();
@ -1287,7 +1350,7 @@ void add_stmt_option_key( sqlsrv_context& ctx, unsigned long key, HashTable* opt
// if a PDO handled option makes it through (such as PDO_ATTR_STATEMENT_CLASS, just skip it
if( option_key != -1 ) {
zval_add_ref( data );
core::sqlsrv_zend_hash_index_update(ctx, options_ht, option_key, (void**)data, sizeof(zval*) TSRMLS_CC );
core::sqlsrv_zend_hash_index_update(ctx, options_ht, option_key, data TSRMLS_CC );
}
}
@ -1300,33 +1363,27 @@ void add_stmt_option_key( sqlsrv_context& ctx, unsigned long key, HashTable* opt
// ctx - The current context.
// stmt_options - The user provided list of statement options.
// pdo_stmt_options_ht - Output hashtable of statement options.
void validate_stmt_options( sqlsrv_context& ctx, zval* stmt_options, __inout HashTable* pdo_stmt_options_ht TSRMLS_DC )
void validate_stmt_options( sqlsrv_context& ctx, zval* stmt_options, _Inout_ HashTable* pdo_stmt_options_ht TSRMLS_DC )
{
try {
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_EXISTANT;
char *key = NULL;
unsigned int key_len = 0;
unsigned long 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_ex( options_ht, &key, &key_len, &int_key, 0, NULL );
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, (void**) &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& ) {
@ -1359,8 +1416,8 @@ void pdo_txn_isolation_conn_attr_func::func( connection_option const* /*option*/
SQLSRV_ASSERT( Z_TYPE_P( value_z ) == IS_STRING, "pdo_txn_isolation_conn_attr_func: Unexpected zval type." );
const char* val = Z_STRVAL_P( value_z );
int val_len = Z_STRLEN_P( value_z );
long out_val = SQL_TXN_READ_COMMITTED;
size_t val_len = Z_STRLEN_P( value_z );
zend_long out_val = SQL_TXN_READ_COMMITTED;
// READ_COMMITTED
if(( val_len == ( sizeof( PDOTxnIsolationValues::READ_COMMITTED ) - 1 )

View file

@ -3,7 +3,7 @@
//
// Contents: initialization routines for PDO_SQLSRV
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -20,6 +20,9 @@
#include "pdo_sqlsrv.h"
#include <psapi.h>
#ifdef ZTS
ZEND_TSRMLS_CACHE_DEFINE();
#endif
ZEND_GET_MODULE(g_pdo_sqlsrv)
extern "C" {
@ -48,19 +51,19 @@ const char PDO_DLL_NAME[] = "php_pdo.dll";
// PHP_DEBUG is always defined as either 0 or 1
#if PHP_DEBUG == 1 && !defined(ZTS)
const char PHP_DLL_NAME[] = "php5_debug.dll";
const char PHP_DLL_NAME[] = "php7_debug.dll";
#elif PHP_DEBUG == 1 && defined(ZTS)
const char PHP_DLL_NAME[] = "php5ts_debug.dll";
const char PHP_DLL_NAME[] = "php7ts_debug.dll";
#elif PHP_DEBUG == 0 && !defined(ZTS)
const char PHP_DLL_NAME[] = "php5.dll";
const char PHP_DLL_NAME[] = "php7.dll";
#elif PHP_DEBUG == 0 && defined(ZTS)
const char PHP_DLL_NAME[] = "php5ts.dll";
const char PHP_DLL_NAME[] = "php7ts.dll";
#else
@ -133,8 +136,14 @@ zend_module_entry g_pdo_sqlsrv_module_entry =
// functions dynamically linked from the PDO (or PHP) dll and called by other parts of the driver
zend_class_entry* (*pdo_get_exception_class)( void );
int (*pdo_subst_named_params)(pdo_stmt_t *stmt, char *inquery, int inquery_len,
char **outquery, int *outquery_len TSRMLS_DC);
int (*pdo_subst_named_params)(pdo_stmt_t *stmt, char *inquery, size_t inquery_len,
char **outquery, size_t *outquery_len TSRMLS_DC);
// called by Zend for each parameter in the g_pdo_errors_ht hash table when it is destroyed
void pdo_error_dtor(zval* elem) {
pdo_error* error_to_ignore = reinterpret_cast<pdo_error*>(Z_PTR_P(elem));
pefree(error_to_ignore, 1);
}
// Module initialization
// This function is called once per execution of the Zend engine
@ -150,6 +159,7 @@ PHP_MINIT_FUNCTION(pdo_sqlsrv)
(ts_allocate_ctor) NULL,
(ts_allocate_dtor) NULL ) == 0 )
return FAILURE;
ZEND_TSRMLS_CACHE_UPDATE();
#endif
core_sqlsrv_register_logger( pdo_sqlsrv_log );
@ -206,8 +216,8 @@ PHP_MINIT_FUNCTION(pdo_sqlsrv)
}
pdo_subst_named_params =
reinterpret_cast<int (*)(pdo_stmt_t *stmt, char *inquery, int inquery_len,
char **outquery, int *outquery_len TSRMLS_DC)>(
reinterpret_cast<int (*)(pdo_stmt_t *stmt, char *inquery, size_t inquery_len,
char **outquery, size_t *outquery_len TSRMLS_DC)>(
GetProcAddress( pdo_hmodule, "pdo_parse_params" ));
if( pdo_subst_named_params == NULL ) {
LOG( SEV_ERROR, "Failed to register driver." );
@ -216,17 +226,13 @@ PHP_MINIT_FUNCTION(pdo_sqlsrv)
// initialize list of pdo errors
g_pdo_errors_ht = reinterpret_cast<HashTable*>( pemalloc( sizeof( HashTable ), 1 ));
int zr = ::zend_hash_init( g_pdo_errors_ht, 50, NULL, NULL, 1 );
if( zr == FAILURE ) {
LOG( SEV_ERROR, "Failed to initialize the PDO errors hashtable." );
return FAILURE;
}
::zend_hash_init( g_pdo_errors_ht, 50, NULL, pdo_error_dtor /*pDestructor*/, 1 );
for( int i = 0; PDO_ERRORS[ i ].error_code != -1; ++i ) {
zr = ::zend_hash_index_update( g_pdo_errors_ht, PDO_ERRORS[ i ].error_code,
&( PDO_ERRORS[ i ].sqlsrv_error ), sizeof( PDO_ERRORS[ i ].sqlsrv_error ), NULL );
if( zr == FAILURE ) {
void* zr = ::zend_hash_index_update_mem( g_pdo_errors_ht, PDO_ERRORS[ i ].error_code,
&( PDO_ERRORS[ i ].sqlsrv_error ), sizeof( PDO_ERRORS[ i ].sqlsrv_error ) );
if( zr == NULL ) {
LOG( SEV_ERROR, "Failed to insert data into PDO errors hashtable." );
return FAILURE;
@ -262,7 +268,6 @@ PHP_MINIT_FUNCTION(pdo_sqlsrv)
return SUCCESS;
}
// Module shutdown function
// Module shutdown function
@ -303,10 +308,12 @@ PHP_RINIT_FUNCTION(pdo_sqlsrv)
SQLSRV_UNUSED( module_number );
SQLSRV_UNUSED( type );
#if defined(ZTS)
ZEND_TSRMLS_CACHE_UPDATE();
#endif
LOG( SEV_NOTICE, "pdo_sqlsrv: entering rinit" );
// verify memory at the end of the request (in debug mode only)
full_mem_check(MEMCHECK_SILENT);
return SUCCESS;
}
@ -321,9 +328,6 @@ PHP_RSHUTDOWN_FUNCTION(pdo_sqlsrv)
LOG( SEV_NOTICE, "pdo_sqlsrv: entering rshutdown" );
// verify memory at the end of the request (in debug mode only)
full_mem_check(MEMCHECK_SILENT);
return SUCCESS;
}
@ -332,12 +336,9 @@ PHP_RSHUTDOWN_FUNCTION(pdo_sqlsrv)
PHP_MINFO_FUNCTION(pdo_sqlsrv)
{
#if defined(ZTS)
SQLSRV_UNUSED( tsrm_ls );
#endif
php_info_print_table_start();
php_info_print_table_header(2, "pdo_sqlsrv support", "enabled");
php_info_print_table_row(2, "ExtensionVer", VER_FILEVERSION_STR);
php_info_print_table_end();
DISPLAY_INI_ENTRIES();
}
@ -371,14 +372,15 @@ namespace {
}
// array of pdo constants.
sqlsrv_attr_pdo_constant pdo_attr_constants[] = {
sqlsrv_attr_pdo_constant pdo_attr_constants[] = {
// driver specific attributes
{ "SQLSRV_ATTR_ENCODING" , SQLSRV_ATTR_ENCODING },
{ "SQLSRV_ATTR_QUERY_TIMEOUT" , SQLSRV_ATTR_QUERY_TIMEOUT },
{ "SQLSRV_ATTR_DIRECT_QUERY" , SQLSRV_ATTR_DIRECT_QUERY },
{ "SQLSRV_ATTR_CURSOR_SCROLL_TYPE" , SQLSRV_ATTR_CURSOR_SCROLL_TYPE },
{ "SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE", SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE },
// driver specific attributes
{ "SQLSRV_ATTR_ENCODING" , SQLSRV_ATTR_ENCODING },
{ "SQLSRV_ATTR_QUERY_TIMEOUT" , SQLSRV_ATTR_QUERY_TIMEOUT },
{ "SQLSRV_ATTR_DIRECT_QUERY" , SQLSRV_ATTR_DIRECT_QUERY },
{ "SQLSRV_ATTR_CURSOR_SCROLL_TYPE" , SQLSRV_ATTR_CURSOR_SCROLL_TYPE },
{ "SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE", SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE },
{ "SQLSRV_ATTR_FETCHES_NUMERIC_TYPE", SQLSRV_ATTR_FETCHES_NUMERIC_TYPE },
// used for the size for output parameters: PDO::PARAM_INT and PDO::PARAM_BOOL use the default size of int,
// PDO::PARAM_STR uses the size of the string in the variable
@ -394,7 +396,7 @@ namespace {
{ "SQLSRV_CURSOR_STATIC" , SQL_CURSOR_STATIC },
{ "SQLSRV_CURSOR_DYNAMIC" , SQL_CURSOR_DYNAMIC },
{ "SQLSRV_CURSOR_KEYSET" , SQL_CURSOR_KEYSET_DRIVEN },
{ "SQLSRV_CURSOR_BUFFERED" , SQLSRV_CURSOR_BUFFERED },
{ "SQLSRV_CURSOR_BUFFERED" , static_cast<int>(SQLSRV_CURSOR_BUFFERED) },
{ NULL , 0 } // terminate the table
};

View file

@ -5,7 +5,7 @@
//
// Copyright Microsoft Corporation
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -22,7 +22,7 @@
#include "pdo_sqlsrv.h"
// Constructor
conn_string_parser:: conn_string_parser( sqlsrv_context& ctx, const char* dsn, int len, __inout HashTable* conn_options_ht )
conn_string_parser:: conn_string_parser( sqlsrv_context& ctx, const char* dsn, int len, _Inout_ HashTable* conn_options_ht )
{
this->conn_str = dsn;
this->len = len;
@ -34,7 +34,7 @@ conn_string_parser:: conn_string_parser( sqlsrv_context& ctx, const char* dsn, i
// Move to the next character
inline bool conn_string_parser::next( void )
{
// if already at the end than return false
// if already at the end then return false
if( this->is_eos() ) {
return false;
@ -108,22 +108,19 @@ bool conn_string_parser::discard_white_spaces()
// Add a key-value pair to the hashtable of connection options.
void conn_string_parser::add_key_value_pair( const char* value, int len TSRMLS_DC )
{
zval_auto_ptr value_z;
ALLOC_INIT_ZVAL( value_z );
zval value_z;
ZVAL_UNDEF( &value_z );
if( len == 0 ) {
ZVAL_STRINGL( value_z, "", 0, 1 /*dup*/ );
ZVAL_STRINGL( &value_z, "", 0);
}
else {
ZVAL_STRINGL( value_z, const_cast<char*>( value ), len, 1 /*dup*/ );
ZVAL_STRINGL( &value_z, const_cast<char*>( value ), len );
}
core::sqlsrv_zend_hash_index_update( *ctx, this->conn_options_ht, this->current_key, (void**)&value_z,
sizeof(zval*) TSRMLS_CC );
zval_add_ref( &value_z );
core::sqlsrv_zend_hash_index_update( *ctx, this->conn_options_ht, this->current_key, &value_z TSRMLS_CC );
}
// Validate a given DSN keyword.
@ -145,7 +142,7 @@ void conn_string_parser::validate_key(const char *key, int key_len TSRMLS_DC )
// encountered an invalid key, throw error.
sqlsrv_malloc_auto_ptr<char> key_name;
key_name = static_cast<char*>( sqlsrv_malloc( new_len + 1 ));
memcpy( key_name, key, new_len );
memcpy_s( key_name, new_len + 1 ,key, new_len );
key_name[ new_len ] = '\0';
THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_INVALID_DSN_KEY, key_name );

View file

@ -6,7 +6,7 @@
//
// Contents: Declarations for the extension
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -48,6 +48,7 @@ enum PDO_SQLSRV_ATTR {
SQLSRV_ATTR_DIRECT_QUERY,
SQLSRV_ATTR_CURSOR_SCROLL_TYPE,
SQLSRV_ATTR_CLIENT_BUFFER_MAX_KB_SIZE,
SQLSRV_ATTR_FETCHES_NUMERIC_TYPE,
};
// valid set of values for TransactionIsolation connection option
@ -70,7 +71,7 @@ extern "C" {
ZEND_BEGIN_MODULE_GLOBALS(pdo_sqlsrv)
unsigned int log_severity;
long client_buffer_max_size;
zend_long client_buffer_max_size;
ZEND_END_MODULE_GLOBALS(pdo_sqlsrv)
@ -158,7 +159,7 @@ class conn_string_parser
void add_key_value_pair( const char* value, int len TSRMLS_DC );
public:
conn_string_parser( sqlsrv_context& ctx, const char* dsn, int len, __inout HashTable* conn_options_ht );
conn_string_parser( sqlsrv_context& ctx, const char* dsn, int len, _Inout_ HashTable* conn_options_ht );
void parse_conn_string( TSRMLS_D );
};
@ -175,7 +176,9 @@ struct pdo_sqlsrv_dbh : public sqlsrv_conn {
zval* stmts;
bool direct_query;
long query_timeout;
long client_buffer_max_size;
zend_long client_buffer_max_size;
SQLSRV_ENCODING bind_param_encoding;
bool fetch_numeric;
pdo_sqlsrv_dbh( SQLHANDLE h, error_callback e, void* driver TSRMLS_DC );
};
@ -210,6 +213,10 @@ struct stmt_option_emulate_prepares : public stmt_option_functor {
virtual void operator()( sqlsrv_stmt* stmt, stmt_option const* /*opt*/, zval* value_z TSRMLS_DC );
};
struct stmt_option_fetch_numeric : public stmt_option_functor {
virtual void operator()( sqlsrv_stmt* stmt, stmt_option const* /*opt*/, zval* value_z TSRMLS_DC );
};
extern struct pdo_stmt_methods pdo_sqlsrv_stmt_methods;
// a core layer pdo stmt object. This object inherits and overrides the callbacks necessary
@ -220,25 +227,28 @@ struct pdo_sqlsrv_stmt : public sqlsrv_stmt {
direct_query( false ),
direct_query_subst_string( NULL ),
direct_query_subst_string_len( 0 ),
bound_column_param_types( NULL )
bound_column_param_types( NULL ),
fetch_numeric( false )
{
pdo_sqlsrv_dbh* db = static_cast<pdo_sqlsrv_dbh*>( c );
direct_query = db->direct_query;
fetch_numeric = db->fetch_numeric;
}
virtual ~pdo_sqlsrv_stmt( void );
// driver specific conversion rules from a SQL Server/ODBC type to one of the SQLSRV_PHPTYPE_* constants
// for PDO, everything is a string, so we return SQLSRV_PHPTYPE_STRING for all SQL types
virtual sqlsrv_phptype sql_type_to_php_type( SQLINTEGER sql_type, SQLUINTEGER size, bool prefer_string_to_stream );
virtual sqlsrv_phptype sql_type_to_php_type( SQLINTEGER sql_type, SQLUINTEGER size, bool prefer_string_to_stream, bool prefer_number_to_string );
bool direct_query; // flag set if the query should be executed directly or prepared
const char* direct_query_subst_string; // if the query is direct, hold the substitution string if using named parameters
int direct_query_subst_string_len; // length of query string used for direct queries
size_t direct_query_subst_string_len; // length of query string used for direct queries
// meta data for current result set
std::vector<field_meta_data*, sqlsrv_allocator< field_meta_data* > > current_meta_data;
pdo_param_type* bound_column_param_types;
bool fetch_numeric;
};
@ -277,7 +287,7 @@ inline void pdo_reset_dbh_error( pdo_dbh_t* dbh TSRMLS_DC )
// release the last statement from the dbh so that error handling won't have a statement passed to it
if( dbh->query_stmt ) {
dbh->query_stmt = NULL;
zend_objects_store_del_ref( &dbh->query_stmt_zval TSRMLS_CC );
zend_objects_store_del( Z_OBJ_P(&dbh->query_stmt_zval TSRMLS_CC) );
}
// if the driver isn't valid, just return (PDO calls close sometimes more than once?)
@ -383,8 +393,8 @@ namespace pdo {
// called pdo_parse_params in php_pdo_driver.h
// we renamed it for 2 reasons: 1) we can't have the same name since it would conflict with our dynamic linking, and
// 2) this is a more precise name
extern int (*pdo_subst_named_params)(pdo_stmt_t *stmt, char *inquery, int inquery_len,
char **outquery, int *outquery_len TSRMLS_DC);
extern int (*pdo_subst_named_params)(pdo_stmt_t *stmt, char *inquery, size_t inquery_len,
char **outquery, size_t *outquery_len TSRMLS_DC);
// logger for pdo_sqlsrv called by the core layer when it wants to log something with the LOG macro
void pdo_sqlsrv_log( unsigned int severity TSRMLS_DC, const char* msg, va_list* print_args );

View file

@ -3,7 +3,7 @@
//
// Contents: Implements the PDOStatement object for the PDO_SQLSRV
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -132,8 +132,8 @@ void set_stmt_cursors( sqlsrv_stmt* stmt, zval* value_z TSRMLS_DC )
THROW_PDO_ERROR( stmt, PDO_SQLSRV_ERROR_INVALID_CURSOR_TYPE );
}
long pdo_cursor_type = Z_LVAL_P( value_z );
int odbc_cursor_type = -1;
zend_long pdo_cursor_type = Z_LVAL_P( value_z );
long odbc_cursor_type = -1;
switch( pdo_cursor_type ) {
@ -164,7 +164,7 @@ void set_stmt_cursor_scroll_type( sqlsrv_stmt* stmt, zval* value_z TSRMLS_DC )
THROW_PDO_ERROR( stmt, PDO_SQLSRV_ERROR_INVALID_CURSOR_WITH_SCROLL_TYPE );
}
long odbc_cursor_type = Z_LVAL_P( value_z );
long odbc_cursor_type = static_cast<long>( Z_LVAL_P( value_z ) );
core_sqlsrv_set_scrollable( stmt, odbc_cursor_type TSRMLS_CC );
@ -181,7 +181,7 @@ void set_stmt_encoding( sqlsrv_stmt* stmt, zval* value_z TSRMLS_DC )
THROW_PDO_ERROR( stmt, PDO_SQLSRV_ERROR_INVALID_ENCODING );
}
long attr_value = Z_LVAL_P( value_z );
zend_long attr_value = Z_LVAL_P( value_z );
switch( attr_value ) {
@ -202,29 +202,31 @@ void set_stmt_encoding( sqlsrv_stmt* stmt, zval* value_z TSRMLS_DC )
// internal helper function to free meta data structures allocated
void meta_data_free( field_meta_data* meta )
{
if( meta->field_name ) {
meta->field_name.reset();
}
sqlsrv_free( meta );
}
zval* convert_to_zval( SQLSRV_PHPTYPE sqlsrv_php_type, void** in_val, SQLLEN field_len )
zval convert_to_zval( SQLSRV_PHPTYPE sqlsrv_php_type, void** in_val, SQLLEN field_len )
{
zval* out_zval = NULL;
zval out_zval;
switch( sqlsrv_php_type ) {
case SQLSRV_PHPTYPE_INT:
case SQLSRV_PHPTYPE_FLOAT:
{
ALLOC_INIT_ZVAL( out_zval );
if( *in_val == NULL ) {
ZVAL_NULL( out_zval );
ZVAL_NULL( &out_zval );
}
else {
if( sqlsrv_php_type == SQLSRV_PHPTYPE_INT ) {
ZVAL_LONG( out_zval, **( reinterpret_cast<long**>( in_val )));
ZVAL_LONG( &out_zval, **( reinterpret_cast<int**>( in_val )));
}
else {
ZVAL_DOUBLE( out_zval, **( reinterpret_cast<double**>( in_val )));
ZVAL_DOUBLE( &out_zval, **( reinterpret_cast<double**>( in_val )));
}
}
@ -238,27 +240,26 @@ zval* convert_to_zval( SQLSRV_PHPTYPE sqlsrv_php_type, void** in_val, SQLLEN fie
case SQLSRV_PHPTYPE_STRING:
case SQLSRV_PHPTYPE_STREAM: // TODO: this will be moved when output streaming is implemented
{
ALLOC_INIT_ZVAL( out_zval );
if( *in_val == NULL ) {
ZVAL_NULL( out_zval );
ZVAL_NULL( &out_zval );
}
else {
ZVAL_STRINGL( out_zval, reinterpret_cast<char*>( *in_val ), field_len, 0 /*duplicate*/ );
ZVAL_STRINGL( &out_zval, reinterpret_cast<char*>( *in_val ), field_len );
sqlsrv_free( *in_val );
}
break;
}
case SQLSRV_PHPTYPE_DATETIME:
DIE( "Unsupported php type" );
out_zval = ( reinterpret_cast<zval*>( *in_val ));
out_zval = *( reinterpret_cast<zval*>( *in_val ));
break;
case SQLSRV_PHPTYPE_NULL:
ALLOC_INIT_ZVAL( out_zval );
ZVAL_NULL( out_zval );
ZVAL_NULL( &out_zval );
break;
default:
@ -274,15 +275,15 @@ zval* convert_to_zval( SQLSRV_PHPTYPE sqlsrv_php_type, void** in_val, SQLLEN fie
int pdo_sqlsrv_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC);
int pdo_sqlsrv_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC);
int pdo_sqlsrv_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori,
long offset TSRMLS_DC);
zend_long offset TSRMLS_DC);
int pdo_sqlsrv_stmt_param_hook(pdo_stmt_t *stmt,
struct pdo_bound_param_data *param, enum pdo_param_event event_type TSRMLS_DC);
int pdo_sqlsrv_stmt_describe_col(pdo_stmt_t *stmt, int colno TSRMLS_DC);
int pdo_sqlsrv_stmt_get_col_data(pdo_stmt_t *stmt, int colno,
char **ptr, unsigned long *len, int *caller_frees TSRMLS_DC);
int pdo_sqlsrv_stmt_set_attr(pdo_stmt_t *stmt, long attr, zval *val TSRMLS_DC);
int pdo_sqlsrv_stmt_get_attr(pdo_stmt_t *stmt, long attr, zval *return_value TSRMLS_DC);
int pdo_sqlsrv_stmt_get_col_meta(pdo_stmt_t *stmt, long colno, zval *return_value TSRMLS_DC);
char **ptr, size_t *len, int *caller_frees TSRMLS_DC);
int pdo_sqlsrv_stmt_set_attr(pdo_stmt_t *stmt, zend_long attr, zval *val TSRMLS_DC);
int pdo_sqlsrv_stmt_get_attr(pdo_stmt_t *stmt, zend_long attr, zval *return_value TSRMLS_DC);
int pdo_sqlsrv_stmt_get_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value TSRMLS_DC);
int pdo_sqlsrv_stmt_next_rowset(pdo_stmt_t *stmt TSRMLS_DC);
int pdo_sqlsrv_stmt_close_cursor(pdo_stmt_t *stmt TSRMLS_DC);
@ -329,6 +330,12 @@ void stmt_option_emulate_prepares:: operator()( sqlsrv_stmt* stmt, stmt_option c
pdo_stmt->supports_placeholders = ( zend_is_true( value_z )) ? PDO_PLACEHOLDER_NONE : PDO_PLACEHOLDER_POSITIONAL;
}
void stmt_option_fetch_numeric:: operator()( sqlsrv_stmt* stmt, stmt_option const* /*opt*/, zval* value_z TSRMLS_DC )
{
pdo_sqlsrv_stmt *pdo_stmt = static_cast<pdo_sqlsrv_stmt*>( stmt );
pdo_stmt->fetch_numeric = ( zend_is_true( value_z )) ? true : false;
}
// log a function entry point
#define PDO_LOG_STMT_ENTRY \
@ -430,13 +437,10 @@ int pdo_sqlsrv_stmt_describe_col(pdo_stmt_t *stmt, int colno TSRMLS_DC)
pdo_column_data* column_data = &(stmt->columns[colno]);
SQLSRV_ASSERT( column_data != NULL, "pdo_sqsrv_stmt_describe_col: pdo_column_data was null" );
// Set the name
column_data->name = reinterpret_cast<char*>( core_meta_data->field_name.get());
core_meta_data->field_name.transferred();
// Set the namelen
column_data->namelen = core_meta_data->field_name_len;
// Set the name
column_data->name = zend_string_init( (const char*)core_meta_data->field_name.get(), core_meta_data->field_name_len, 0 );
core_meta_data->field_name.reset();
// Set the maxlen
column_data->maxlen = ( core_meta_data->field_precision > 0 ) ? core_meta_data->field_precision : core_meta_data->field_size;
@ -519,7 +523,7 @@ int pdo_sqlsrv_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC)
if( driver_stmt->direct_query ) {
query = driver_stmt->direct_query_subst_string;
query_len = driver_stmt->direct_query_subst_string_len;
query_len = static_cast<unsigned int>( driver_stmt->direct_query_subst_string_len );
}
// if the user is using prepare emulation (PDO::ATTR_EMULATE_PREPARES), set the query to the
@ -527,7 +531,7 @@ int pdo_sqlsrv_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC)
if( stmt->supports_placeholders == PDO_PLACEHOLDER_NONE ) {
query = stmt->active_query_string;
query_len = stmt->active_query_stringlen;
query_len = static_cast<unsigned int>( stmt->active_query_stringlen );
}
core_sqlsrv_execute( driver_stmt TSRMLS_CC, query, query_len );
@ -580,7 +584,7 @@ int pdo_sqlsrv_stmt_execute(pdo_stmt_t *stmt TSRMLS_DC)
// Return:
// 0 for failure, 1 for success.
int pdo_sqlsrv_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori,
long offset TSRMLS_DC)
zend_long offset TSRMLS_DC)
{
PDO_RESET_STMT_ERROR;
PDO_VALIDATE_STMT;
@ -609,10 +613,9 @@ int pdo_sqlsrv_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori,
}
for( long i = 0; i < stmt->column_count; ++i ) {
if( zend_hash_index_find( stmt->bound_columns, i, (void**) &bind_data ) == FAILURE &&
zend_hash_find( stmt->bound_columns, stmt->columns[ i ].name, stmt->columns[ i ].namelen,
(void**) &bind_data ) == FAILURE ) {
if (NULL== (bind_data = reinterpret_cast<pdo_bound_param_data*>(zend_hash_index_find_ptr(stmt->bound_columns, i))) &&
(NULL == (bind_data = reinterpret_cast<pdo_bound_param_data*>(zend_hash_find_ptr(stmt->bound_columns, stmt->columns[i].name))))) {
driver_stmt->bound_column_param_types[ i ] = PDO_PARAM_ZVAL;
continue;
@ -675,7 +678,7 @@ int pdo_sqlsrv_stmt_fetch(pdo_stmt_t *stmt, enum pdo_fetch_orientation ori,
// Return:
// 0 for failure, 1 for success.
int pdo_sqlsrv_stmt_get_col_data(pdo_stmt_t *stmt, int colno,
char **ptr, unsigned long *len, int *caller_frees TSRMLS_DC)
char **ptr, size_t *len, int *caller_frees TSRMLS_DC)
{
PDO_RESET_STMT_ERROR;
PDO_VALIDATE_STMT;
@ -703,8 +706,9 @@ int pdo_sqlsrv_stmt_get_col_data(pdo_stmt_t *stmt, int colno,
sqlsrv_phptype sqlsrv_php_type;
SQLSRV_ASSERT( colno >= 0 && colno < static_cast<int>( driver_stmt->current_meta_data.size()),
"Invalid column number in pdo_sqlsrv_stmt_get_col_data" );
sqlsrv_php_type = driver_stmt->sql_type_to_php_type( driver_stmt->current_meta_data[ colno ]->field_type,
driver_stmt->current_meta_data[ colno ]->field_size, true );
sqlsrv_php_type = driver_stmt->sql_type_to_php_type( static_cast<SQLUINTEGER>( driver_stmt->current_meta_data[ colno ]->field_type ),
static_cast<SQLUINTEGER>( driver_stmt->current_meta_data[ colno ]->field_size ),
true, driver_stmt->fetch_numeric );
// set the encoding if the user specified one via bindColumn, otherwise use the statement's encoding
sqlsrv_php_type.typeinfo.encoding = driver_stmt->encoding();
@ -718,11 +722,11 @@ int pdo_sqlsrv_stmt_get_col_data(pdo_stmt_t *stmt, int colno,
TSRMLS_CC );
pdo_bound_param_data* bind_data = NULL;
int zr = zend_hash_index_find( stmt->bound_columns, colno, (void**) &bind_data );
bind_data = reinterpret_cast<pdo_bound_param_data*>(zend_hash_index_find_ptr(stmt->bound_columns, colno));
if( bind_data != NULL && bind_data->driver_params != NULL ) {
if( bind_data != NULL && !Z_ISUNDEF(bind_data->driver_params) ) {
CHECK_CUSTOM_ERROR( Z_TYPE_P( bind_data->driver_params ) != IS_LONG, driver_stmt,
CHECK_CUSTOM_ERROR( Z_TYPE( bind_data->driver_params ) != IS_LONG, driver_stmt,
PDO_SQLSRV_ERROR_INVALID_COLUMN_DRIVER_DATA, colno + 1 ) {
throw pdo::PDOException();
}
@ -734,7 +738,7 @@ int pdo_sqlsrv_stmt_get_col_data(pdo_stmt_t *stmt, int colno,
throw pdo::PDOException();
}
sqlsrv_php_type.typeinfo.encoding = Z_LVAL_P( bind_data->driver_params );
sqlsrv_php_type.typeinfo.encoding = Z_LVAL( bind_data->driver_params );
switch( sqlsrv_php_type.typeinfo.encoding ) {
case SQLSRV_ENCODING_SYSTEM:
@ -749,10 +753,11 @@ 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* )));
*zval_ptr = reinterpret_cast<zval*>( convert_to_zval( sqlsrv_phptype_out, reinterpret_cast<void**>( ptr ), *len ));
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 );
*len = sizeof( zval );
@ -776,12 +781,13 @@ int pdo_sqlsrv_stmt_get_col_data(pdo_stmt_t *stmt, int colno,
// val - Attribute value.
// Return:
// 0 for failure, 1 for success.
int pdo_sqlsrv_stmt_set_attr(pdo_stmt_t *stmt, long attr, zval *val TSRMLS_DC)
int pdo_sqlsrv_stmt_set_attr(pdo_stmt_t *stmt, zend_long attr, zval *val TSRMLS_DC)
{
PDO_RESET_STMT_ERROR;
PDO_VALIDATE_STMT;
PDO_LOG_STMT_ENTRY;
pdo_sqlsrv_stmt* pdo_stmt = static_cast<pdo_sqlsrv_stmt*>( stmt->driver_data );
sqlsrv_stmt* driver_stmt = static_cast<sqlsrv_stmt*>( stmt->driver_data );
try {
@ -812,6 +818,10 @@ int pdo_sqlsrv_stmt_set_attr(pdo_stmt_t *stmt, long attr, zval *val TSRMLS_DC)
core_sqlsrv_set_buffered_query_limit( driver_stmt, val TSRMLS_CC );
break;
case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE:
pdo_stmt->fetch_numeric = ( zend_is_true( val )) ? true : false;
break;
default:
THROW_PDO_ERROR( driver_stmt, PDO_SQLSRV_ERROR_INVALID_STMT_ATTR );
break;
@ -837,7 +847,7 @@ int pdo_sqlsrv_stmt_set_attr(pdo_stmt_t *stmt, long attr, zval *val TSRMLS_DC)
// return_value - Attribute value.
// Return:
// 0 for failure, 1 for success.
int pdo_sqlsrv_stmt_get_attr( pdo_stmt_t *stmt, long attr, zval *return_value TSRMLS_DC )
int pdo_sqlsrv_stmt_get_attr( pdo_stmt_t *stmt, zend_long attr, zval *return_value TSRMLS_DC )
{
PDO_RESET_STMT_ERROR;
PDO_VALIDATE_STMT;
@ -887,6 +897,12 @@ int pdo_sqlsrv_stmt_get_attr( pdo_stmt_t *stmt, long attr, zval *return_value TS
break;
}
case SQLSRV_ATTR_FETCHES_NUMERIC_TYPE:
{
ZVAL_BOOL( return_value, driver_stmt->fetch_numeric );
break;
}
default:
THROW_PDO_ERROR( driver_stmt, PDO_SQLSRV_ERROR_INVALID_STMT_ATTR );
break;
@ -915,7 +931,7 @@ int pdo_sqlsrv_stmt_get_attr( pdo_stmt_t *stmt, long attr, zval *return_value TS
// return_value - zval* consisting of the metadata.
// Return:
// 0 for failure, 1 for success.
int pdo_sqlsrv_stmt_get_col_meta(pdo_stmt_t *stmt, long colno, zval *return_value TSRMLS_DC)
int pdo_sqlsrv_stmt_get_col_meta(pdo_stmt_t *stmt, zend_long colno, zval *return_value TSRMLS_DC)
{
PDO_RESET_STMT_ERROR;
PDO_VALIDATE_STMT;
@ -943,7 +959,7 @@ int pdo_sqlsrv_stmt_get_col_meta(pdo_stmt_t *stmt, long colno, zval *return_valu
SQLLEN not_used;
core::SQLColAttribute( driver_stmt, (SQLUSMALLINT) colno + 1, SQL_DESC_TYPE_NAME, field_type_name,
sizeof( field_type_name ), &out_buff_len, &not_used TSRMLS_CC );
add_assoc_string( return_value, "sqlsrv:decl_type", field_type_name, 1 );
add_assoc_string( return_value, "sqlsrv:decl_type", field_type_name );
// get the PHP type of the column. The types returned here mirror the types returned by debug_zval_dump when
// given a variable of the same type. However, debug_zval_dump also gives the length of a string, and we only
@ -951,7 +967,7 @@ int pdo_sqlsrv_stmt_get_col_meta(pdo_stmt_t *stmt, long colno, zval *return_valu
long pdo_type = sql_type_to_pdo_type( core_meta_data->field_type );
switch( pdo_type ) {
case PDO_PARAM_STR:
add_assoc_string( return_value, "native_type", "string", 1 );
add_assoc_string( return_value, "native_type", "string" );
break;
default:
DIE( "pdo_sqlsrv_stmt_get_col_data: Unknown PDO type returned" );
@ -963,7 +979,7 @@ int pdo_sqlsrv_stmt_get_col_meta(pdo_stmt_t *stmt, long colno, zval *return_valu
SQLLEN field_type_num;
core::SQLColAttribute( driver_stmt, (SQLUSMALLINT) colno + 1, SQL_DESC_TABLE_NAME, table_name, SQL_SERVER_IDENT_SIZE_MAX,
&out_buff_len, &field_type_num TSRMLS_CC );
add_assoc_string( return_value, "table", table_name, 1 );
add_assoc_string( return_value, "table", table_name );
if( stmt->columns[ colno ].param_type == PDO_PARAM_ZVAL ) {
add_assoc_long( return_value, "pdo_type", pdo_type );
@ -1061,6 +1077,11 @@ int pdo_sqlsrv_stmt_param_hook(pdo_stmt_t *stmt,
// since the param isn't reliable, we don't do anything here
case PDO_PARAM_EVT_ALLOC:
// if emulate prepare is on, set the bind_param_encoding so it can be used in PDO::quote when binding parameters on the client side
if ( stmt->supports_placeholders == PDO_PLACEHOLDER_NONE ) {
pdo_sqlsrv_dbh* driver_dbh = reinterpret_cast<pdo_sqlsrv_dbh*>( stmt->dbh->driver_data );
driver_dbh->bind_param_encoding = static_cast<SQLSRV_ENCODING>( Z_LVAL( param->driver_params ));
}
break;
case PDO_PARAM_EVT_FREE:
break;
@ -1122,10 +1143,10 @@ int pdo_sqlsrv_stmt_param_hook(pdo_stmt_t *stmt,
// since the core layer keys off the zval type, we substitute a null for what they gave us
case PDO_PARAM_NULL:
{
zval* null_zval;
zval null_zval;
php_out_type = SQLSRV_PHPTYPE_NULL;
MAKE_STD_ZVAL( null_zval );
ZVAL_NULL( null_zval );
ZVAL_NULL( &null_zval );
zval_ptr_dtor( &param->parameter );
param->parameter = null_zval;
break;
@ -1171,7 +1192,7 @@ int pdo_sqlsrv_stmt_param_hook(pdo_stmt_t *stmt,
// that succeeds since the core layer implements DateTime object handling for the sqlsrv
// 2.0 driver. To be consistent and avoid surprises of one object type working and others
// not, we block all objects here.
CHECK_CUSTOM_ERROR( direction != SQL_PARAM_OUTPUT && Z_TYPE_P( param->parameter ) == IS_OBJECT,
CHECK_CUSTOM_ERROR( direction != SQL_PARAM_OUTPUT && Z_TYPE( param->parameter ) == IS_OBJECT,
driver_stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, param->paramno + 1 ) {
throw pdo::PDOException();
}
@ -1182,8 +1203,8 @@ int pdo_sqlsrv_stmt_param_hook(pdo_stmt_t *stmt,
encoding = driver_stmt->conn->encoding();
}
// if the user provided an encoding, use it instead
if( param->driver_params != NULL ) {
CHECK_CUSTOM_ERROR( Z_TYPE_P( param->driver_params ) != IS_LONG, driver_stmt,
if( !Z_ISUNDEF(param->driver_params) ) {
CHECK_CUSTOM_ERROR( Z_TYPE( param->driver_params ) != IS_LONG, driver_stmt,
PDO_SQLSRV_ERROR_INVALID_DRIVER_PARAM ) {
throw pdo::PDOException();
}
@ -1191,7 +1212,7 @@ int pdo_sqlsrv_stmt_param_hook(pdo_stmt_t *stmt,
PDO_SQLSRV_ERROR_INVALID_DRIVER_PARAM_TYPE, param->paramno + 1 ) {
throw pdo::PDOException();
}
encoding = static_cast<SQLSRV_ENCODING>( Z_LVAL_P( param->driver_params ));
encoding = static_cast<SQLSRV_ENCODING>( Z_LVAL( param->driver_params ));
switch( encoding ) {
case SQLSRV_ENCODING_SYSTEM:
@ -1205,7 +1226,7 @@ int pdo_sqlsrv_stmt_param_hook(pdo_stmt_t *stmt,
}
}
// and bind the parameter
core_sqlsrv_bind_param( driver_stmt, param->paramno, direction, param->parameter, php_out_type, encoding,
core_sqlsrv_bind_param( driver_stmt, static_cast<SQLUSMALLINT>( param->paramno ), direction, &(param->parameter) , php_out_type, encoding,
sql_type, column_size, decimal_digits TSRMLS_CC );
}
break;
@ -1219,9 +1240,9 @@ int pdo_sqlsrv_stmt_param_hook(pdo_stmt_t *stmt,
if( !param->is_param ) {
break;
}
core_sqlsrv_post_param( reinterpret_cast<sqlsrv_stmt*>( stmt->driver_data ), param->paramno,
param->parameter TSRMLS_CC );
&(param->parameter) TSRMLS_CC );
}
break;
case PDO_PARAM_EVT_FETCH_PRE:
@ -1249,7 +1270,7 @@ int pdo_sqlsrv_stmt_param_hook(pdo_stmt_t *stmt,
// Returns a sqlsrv_phptype for a given SQL Server data type.
sqlsrv_phptype pdo_sqlsrv_stmt::sql_type_to_php_type( SQLINTEGER sql_type, SQLUINTEGER size, bool prefer_string_over_stream )
sqlsrv_phptype pdo_sqlsrv_stmt::sql_type_to_php_type( SQLINTEGER sql_type, SQLUINTEGER size, bool prefer_string_over_stream, bool prefer_number_to_string )
{
sqlsrv_phptype sqlsrv_phptype;
int local_encoding = this->encoding();
@ -1261,15 +1282,31 @@ sqlsrv_phptype pdo_sqlsrv_stmt::sql_type_to_php_type( SQLINTEGER sql_type, SQLUI
}
switch( sql_type ) {
case SQL_BIT:
case SQL_INTEGER:
case SQL_SMALLINT:
case SQL_TINYINT:
case SQL_BIT:
case SQL_INTEGER:
case SQL_SMALLINT:
case SQL_TINYINT:
if ( prefer_number_to_string ) {
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_INT;
}
else {
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING;
sqlsrv_phptype.typeinfo.encoding = local_encoding;
}
break;
case SQL_FLOAT:
case SQL_REAL:
if ( prefer_number_to_string ) {
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_FLOAT;
}
else {
sqlsrv_phptype.typeinfo.type = SQLSRV_PHPTYPE_STRING;
sqlsrv_phptype.typeinfo.encoding = local_encoding;
}
break;
case SQL_BIGINT:
case SQL_CHAR:
case SQL_DECIMAL:
case SQL_FLOAT:
case SQL_REAL:
case SQL_GUID:
case SQL_NUMERIC:
case SQL_WCHAR:

View file

@ -3,7 +3,7 @@
//
// Contents: Utility functions used by both connection or statement functions
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -367,15 +367,14 @@ pdo_error PDO_ERRORS[] = {
SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED,
{ IMSSP, (SQLCHAR*) "Memory limit of %1!d! KB exceeded for buffered query", -71, true }
},
{ -1, {} }
{ UINT_MAX, {} }
};
// Returns a sqlsrv_error for a given error code.
sqlsrv_error_const* get_error_message( unsigned int sqlsrv_error_code ) {
sqlsrv_error_const *error_message = NULL;
int zr = zend_hash_index_find( g_pdo_errors_ht, sqlsrv_error_code, reinterpret_cast<void**>( &error_message ));
sqlsrv_error_const* get_error_message(unsigned int sqlsrv_error_code) {
sqlsrv_error_const *error_message = NULL;
int zr = (error_message = reinterpret_cast<sqlsrv_error_const*>(zend_hash_index_find_ptr(g_pdo_errors_ht, sqlsrv_error_code))) != NULL ? SUCCESS : FAILURE;
if( zr == FAILURE ) {
DIE( "get_error_message: zend_hash_index_find returned failure for sqlsrv_error_code = %1!d!", sqlsrv_error_code );
}
@ -446,8 +445,8 @@ bool pdo_sqlsrv_handle_dbh_error( sqlsrv_context& ctx, unsigned int sqlsrv_error
SQLSRV_ASSERT( err == true, "No ODBC error was found" );
}
SQLSRV_STATIC_ASSERT( sizeof( error->sqlstate ) <= sizeof( dbh->error_code ));
strcpy_s( dbh->error_code, sizeof( dbh->error_code ), reinterpret_cast<const char*>( error->sqlstate ));
SQLSRV_ASSERT(strlen(reinterpret_cast<const char*>(error->sqlstate)) <= sizeof(dbh->error_code), "Error code overflow");
strcpy_s(dbh->error_code, sizeof(dbh->error_code), reinterpret_cast<const char*>(error->sqlstate));
switch( dbh->error_mode ) {
case PDO_ERRMODE_EXCEPTION:
@ -459,11 +458,10 @@ bool pdo_sqlsrv_handle_dbh_error( sqlsrv_context& ctx, unsigned int sqlsrv_error
break;
case PDO_ERRMODE_WARNING:
if( !warning ) {
unsigned int msg_len = strlen( reinterpret_cast<const char*>( error->native_message )) + SQL_SQLSTATE_BUFSIZE
size_t msg_len = strlen( reinterpret_cast<const char*>( error->native_message )) + SQL_SQLSTATE_BUFSIZE
+ MAX_DIGITS + 1;
sqlsrv_malloc_auto_ptr<char> msg;
msg = static_cast<char*>( sqlsrv_malloc( msg_len ));
core_sqlsrv_format_message( msg, msg_len, WARNING_TEMPLATE, error->sqlstate, error->native_code,
char* msg = static_cast<char*>( sqlsrv_malloc( msg_len ));
core_sqlsrv_format_message( msg, static_cast<unsigned int>( msg_len ), WARNING_TEMPLATE, error->sqlstate, error->native_code,
error->native_message );
php_error( E_WARNING, msg );
sqlsrv_free( msg );
@ -499,7 +497,7 @@ bool pdo_sqlsrv_handle_stmt_error( sqlsrv_context& ctx, unsigned int sqlsrv_erro
SQLSRV_ASSERT( err == true, "No ODBC error was found" );
}
SQLSRV_STATIC_ASSERT( sizeof( error->sqlstate ) <= sizeof( pdo_stmt->error_code ));
SQLSRV_ASSERT( strlen( reinterpret_cast<const char*>( error->sqlstate ) ) <= sizeof( pdo_stmt->error_code ), "Error code overflow");
strcpy_s( pdo_stmt->error_code, sizeof( pdo_stmt->error_code ), reinterpret_cast<const char*>( error->sqlstate ));
switch( pdo_stmt->dbh->error_mode ) {
@ -512,11 +510,10 @@ bool pdo_sqlsrv_handle_stmt_error( sqlsrv_context& ctx, unsigned int sqlsrv_erro
break;
case PDO_ERRMODE_WARNING:
if( !warning ) {
unsigned int msg_len = strlen( reinterpret_cast<const char*>( error->native_message )) + SQL_SQLSTATE_BUFSIZE
size_t msg_len = strlen( reinterpret_cast<const char*>( error->native_message )) + SQL_SQLSTATE_BUFSIZE
+ MAX_DIGITS + 1;
sqlsrv_malloc_auto_ptr<char> msg;
msg = static_cast<char*>( sqlsrv_malloc( msg_len ));
core_sqlsrv_format_message( msg, msg_len, WARNING_TEMPLATE, error->sqlstate, error->native_code,
char* msg = static_cast<char*>( sqlsrv_malloc(SQL_MAX_MESSAGE_LENGTH+1));
core_sqlsrv_format_message( msg, static_cast<unsigned int>( msg_len ), WARNING_TEMPLATE, error->sqlstate, error->native_code,
error->native_message );
php_error( E_WARNING, msg );
sqlsrv_free( msg );
@ -547,13 +544,8 @@ void pdo_sqlsrv_retrieve_context_error( sqlsrv_error const* last_error, zval* pd
// SQLSTATE is already present in the zval.
add_next_index_long( pdo_zval, last_error->native_code );
add_next_index_string( pdo_zval, reinterpret_cast<char*>( last_error->native_message ), 1 /*dup*/ );
add_next_index_string( pdo_zval, reinterpret_cast<char*>( last_error->native_message ));
}
else {
add_next_index_null( pdo_zval ); /* native code */
add_next_index_null( pdo_zval ); /* native message */
}
}
// Formats the error message and writes to the php error log.
@ -577,11 +569,11 @@ namespace {
void pdo_sqlsrv_throw_exception( sqlsrv_error_const* error TSRMLS_DC )
{
zval_auto_ptr ex_obj;
MAKE_STD_ZVAL( ex_obj );
zval ex_obj;
ZVAL_UNDEF( &ex_obj );
zend_class_entry* ex_class = pdo_get_exception_class();
int zr = object_init_ex( ex_obj, ex_class );
int zr = object_init_ex( &ex_obj, ex_class );
SQLSRV_ASSERT( zr != FAILURE, "Failed to initialize exception object" );
sqlsrv_malloc_auto_ptr<char> ex_msg;
@ -589,23 +581,27 @@ void pdo_sqlsrv_throw_exception( sqlsrv_error_const* error TSRMLS_DC )
12 + 1; // 12 = "SQLSTATE[]: "
ex_msg = reinterpret_cast<char*>( sqlsrv_malloc( ex_msg_len ));
snprintf( ex_msg, ex_msg_len, EXCEPTION_MSG_TEMPLATE, error->sqlstate, error->native_message );
zend_update_property_string( ex_class, ex_obj, EXCEPTION_PROPERTY_MSG, sizeof( EXCEPTION_PROPERTY_MSG ) - 1,
zend_update_property_string( ex_class, &ex_obj, EXCEPTION_PROPERTY_MSG, sizeof( EXCEPTION_PROPERTY_MSG ) - 1,
ex_msg TSRMLS_CC );
zend_update_property_string( ex_class, ex_obj, EXCEPTION_PROPERTY_CODE, sizeof( EXCEPTION_PROPERTY_CODE ) - 1,
zend_update_property_string( ex_class, &ex_obj, EXCEPTION_PROPERTY_CODE, sizeof( EXCEPTION_PROPERTY_CODE ) - 1,
reinterpret_cast<char*>( error->sqlstate ) TSRMLS_CC );
zval_auto_ptr ex_error_info;
MAKE_STD_ZVAL( ex_error_info );
array_init( ex_error_info );
add_next_index_string( ex_error_info, reinterpret_cast<char*>( error->sqlstate ), 1 /* dup */ );
add_next_index_long( ex_error_info, error->native_code );
add_next_index_string( ex_error_info, reinterpret_cast<char*>( error->native_message ), 1 /* dup */ );
zend_update_property( ex_class, ex_obj, EXCEPTION_PROPERTY_ERRORINFO, sizeof( EXCEPTION_PROPERTY_ERRORINFO ) - 1,
ex_error_info TSRMLS_CC );
zval ex_error_info;
ZVAL_UNDEF( &ex_error_info );
array_init( &ex_error_info );
add_next_index_string( &ex_error_info, reinterpret_cast<char*>( error->sqlstate ));
add_next_index_long( &ex_error_info, error->native_code );
add_next_index_string( &ex_error_info, reinterpret_cast<char*>( error->native_message ));
//zend_update_property makes an entry in the properties_table in ex_obj point to the Z_ARRVAL( ex_error_info )
//and the refcount of the zend_array is incremented by 1
zend_update_property( ex_class, &ex_obj, EXCEPTION_PROPERTY_ERRORINFO, sizeof( EXCEPTION_PROPERTY_ERRORINFO ) - 1,
&ex_error_info TSRMLS_CC );
zend_throw_exception_object( ex_obj TSRMLS_CC );
ex_msg.transferred();
ex_obj.transferred();
//DELREF ex_error_info here to decrement the refcount of the zend_array is 1
//the global hashtable EG(exception) then points to the zend_object in ex_obj in zend_throw_exception_object;
//this ensure when EG(exception) cleans itself at php shutdown, the zend_array allocated is properly destroyed
Z_DELREF( ex_error_info );
zend_throw_exception_object( &ex_obj TSRMLS_CC );
}
}

View file

@ -3,7 +3,7 @@
//
// Contents: Version resource
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.0 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -59,7 +59,7 @@ BEGIN
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "Comments", "This product includes PHP software that is freely available from http://www.php.net/software/. © 1997-2009 The PHP Group. All rights reserved.\0"
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 (PDO Driver)\0"
VALUE "FileVersion", STRVER4(SQLVERSION_MAJOR,SQLVERSION_MINOR, SQLVERSION_MMDD, SQLVERSION_REVISION)

View file

@ -2,7 +2,7 @@
// File: version.h
// Contents: Version number constants
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -16,10 +16,10 @@
// IN THE SOFTWARE.
//---------------------------------------------------------------------------------------------------------------------------------
#define VER_FILEVERSION_STR "3.2.0.0"
#define _FILEVERSION 3,2,0,0
#define SQLVERSION_MAJOR 3
#define SQLVERSION_MINOR 2
#define VER_FILEVERSION_STR "4.1.0.0"
#define _FILEVERSION 4,1,0,0
#define SQLVERSION_MAJOR 4
#define SQLVERSION_MINOR 1
#define SQLVERSION_MMDD 0
#define SQLVERSION_REVISION 0

View file

@ -0,0 +1,74 @@
<?php
echo "\n";
$serverName = "tcp:yourserver.database.windows.net,1433";
$database = "yourdatabase";
$uid = "yourusername";
$pwd = "yourpassword";
//Establishes the connection
$conn = new PDO( "sqlsrv:server=$serverName ; Database = $database", $uid, $pwd);
//Select Query
$tsql = "SELECT [CompanyName] FROM SalesLT.Customer";
//Executes the query
$getProducts = $conn->query( $tsql );
//Error handling
FormatErrors ($conn->errorInfo());
$productCount = 0;
$ctr = 0;
?>
<h1> First 10 results are : </h1>
<?php
while($row = $getProducts->fetch(PDO::FETCH_ASSOC))
{
if($ctr>9)
break;
$ctr++;
echo($row['CompanyName']);
echo("<br/>");
$productCount++;
}
$getProducts = NULL;
$tsql = "INSERT INTO SalesLT.Product (Name, ProductNumber, StandardCost, ListPrice, SellStartDate) OUTPUT INSERTED.* VALUES ('SQL New 1', 'SQL New 2', 0, 0, getdate())";
//Insert query
$insertReview = $conn->query( $tsql );
FormatErrors ($conn->errorInfo());
?>
<h1> Product Key inserted is :</h1>
<?php
while($row = $insertReview->fetch(PDO::FETCH_ASSOC))
{
echo($row['ProductID']."<br/>");
}
$insertReview = NULL;
//Delete Query
//We are deleting the same record
$tsql = "DELETE FROM [SalesLT].[Product] WHERE Name=?";
$param = "SQL New 1";
$deleteReview = $conn->prepare($tsql);
$deleteReview->bindParam(1, $param);
$deleteReview->execute();
FormatErrors ($deleteReview->errorInfo());
function FormatErrors( $error )
{
/* Display error. */
echo "Error information: <br/>";
echo "SQLSTATE: ".$error[0]."<br/>";
echo "Code: ".$error[1]."<br/>";
echo "Message: ".$error[2]."<br/>";
}
?>

69
sample/sqlsrv_sample.php Normal file
View file

@ -0,0 +1,69 @@
<?php
echo "\n";
$serverName = "tcp:yourserver.database.windows.net,1433";
$connectionOptions = array("Database"=>"yourdatabase", "Uid"=>"yourusername", "PWD"=>"yourpassword");
//Establishes the connection
$conn = sqlsrv_connect($serverName, $connectionOptions);
//Select Query
$tsql = "SELECT [CompanyName] FROM SalesLT.Customer";
//Executes the query
$getProducts = sqlsrv_query($conn, $tsql);
//Error handling
if ($getProducts == FALSE)
die(FormatErrors(sqlsrv_errors()));
$productCount = 0;
$ctr = 0;
?>
<h1> First 10 results are : </h1>
<?php
while($row = sqlsrv_fetch_array($getProducts, SQLSRV_FETCH_ASSOC))
{
if($ctr>9)
break;
$ctr++;
echo($row['CompanyName']);
echo("<br/>");
$productCount++;
}
sqlsrv_free_stmt($getProducts);
$tsql = "INSERT INTO SalesLT.Product (Name, ProductNumber, StandardCost, ListPrice, SellStartDate) OUTPUT INSERTED.ProductID VALUES ('SQL New 1', 'SQL New 2', 0, 0, getdate())";
//Insert query
$insertReview = sqlsrv_query($conn, $tsql);
if($insertReview == FALSE)
die(FormatErrors( sqlsrv_errors()));
?>
<h1> Product Key inserted is :</h1>
<?php
while($row = sqlsrv_fetch_array($insertReview, SQLSRV_FETCH_ASSOC))
{
echo($row['ProductID']);
}
sqlsrv_free_stmt($insertReview);
//Delete Query
//We are deleting the same record
$tsql = "DELETE FROM [SalesLT].[Product] WHERE Name=?";
$params = array("SQL New 1");
$deleteReview = sqlsrv_prepare($conn, $tsql, $params);
if($deleteReview == FALSE)
die(FormatErrors(sqlsrv_errors()));
if(sqlsrv_execute($deleteReview) == FALSE)
die(FormatErrors(sqlsrv_errors()));
function FormatErrors( $errors )
{
/* Display errors. */
echo "Error information: <br/>";
foreach ( $errors as $error )
{
echo "SQLSTATE: ".$error['SQLSTATE']."<br/>";
echo "Code: ".$error['code']."<br/>";
echo "Message: ".$error['message']."<br/>";
}
}
?>

View file

@ -1 +1 @@
Microsoft Drivers 3.2.0 for PHP for SQL Server (SQLSRV driver)
Microsoft Drivers 4.1 for PHP for SQL Server (PDO driver)

View file

@ -3,7 +3,7 @@
//
// Contents: JScript build configuration used by buildconf.bat
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.0 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -21,16 +21,17 @@ ARG_ENABLE("sqlsrv", "enable Microsoft Drivers for PHP for SQL Server (SQLSRV dr
if( PHP_SQLSRV != "no" ) {
sqlsrv_src = "conn.cpp init.cpp stmt.cpp util.cpp core_init.cpp core_conn.cpp core_stmt.cpp core_util.cpp core_stream.cpp core_results.cpp";
if (CHECK_LIB("odbc32.lib", "sqlsrv") && CHECK_LIB("odbccp32.lib", "sqlsrv") &&
CHECK_LIB("version.lib", "sqlsrv") && CHECK_LIB("psapi.lib", "sqlsrv")) {
EXTENSION("sqlsrv", "conn.cpp init.cpp stmt.cpp util.cpp core_init.cpp core_conn.cpp core_stmt.cpp core_util.cpp core_stream.cpp core_results.cpp" )
EXTENSION("sqlsrv", sqlsrv_src, PHP_SQLSRV_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1")
CHECK_HEADER_ADD_INCLUDE('sql.h', 'CFLAGS_SQLSRV_ODBC');
CHECK_HEADER_ADD_INCLUDE('sqlext.h', 'CFLAGS_SQLSRV_ODBC');
ADD_FLAG( 'LDFLAGS_SQLSRV', '/NXCOMPAT /DYNAMICBASE /debug' );
ADD_FLAG( 'CFLAGS_SQLSRV', '/D ZEND_WIN32_FORCE_INLINE' );
ADD_FLAG( 'CFLAGS_SQLSRV', '/D _HAS_CPP0X=0' );
ADD_FLAG( 'CFLAGS_SQLSRV', '/EHsc' );
ADD_FLAG( 'CFLAGS_SQLSRV', '/GS' );
ADD_FLAG( 'CFLAGS_SQLSRV', '/Zi' );

View file

@ -3,7 +3,7 @@
//
// Contents: Routines that use connection handles
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -55,25 +55,25 @@ struct conn_char_set_func {
{
convert_to_string( value );
const char* encoding = Z_STRVAL_P( value );
unsigned int encoding_len = Z_STRLEN_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;
core::sqlsrv_zend_hash_get_current_data( *conn, g_ss_encodings_ht, (void**) &ss_encoding TSRMLS_CC );
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( !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 );
}
@ -105,8 +105,7 @@ struct int_conn_attr_func {
{
try {
core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( Z_LVAL_P( value )),
SQL_IS_UINTEGER TSRMLS_CC );
core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( Z_LVAL_P( value )), SQL_IS_UINTEGER TSRMLS_CC );
}
catch( core::CoreException& ) {
throw;
@ -120,9 +119,9 @@ struct bool_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>((zend_long)zend_is_true(value)),
SQL_IS_UINTEGER TSRMLS_CC);
core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( zend_is_true( value )),
SQL_IS_UINTEGER TSRMLS_CC );
}
catch( core::CoreException& ) {
throw;
@ -134,14 +133,14 @@ struct bool_conn_attr_func {
//// *** internal functions ***
void sqlsrv_conn_close_stmts( ss_sqlsrv_conn* conn TSRMLS_DC );
void validate_conn_options( sqlsrv_context& ctx, zval* user_options_z, __out char** uid, __out char** pwd,
__inout HashTable* ss_conn_options_ht TSRMLS_DC );
void validate_stmt_options( sqlsrv_context& ctx, zval* stmt_options, __inout HashTable* ss_stmt_options_ht TSRMLS_DC );
void add_conn_option_key( sqlsrv_context& ctx, char* key, unsigned int key_len,
HashTable* options_ht, zval** data TSRMLS_DC );
void add_stmt_option_key( sqlsrv_context& ctx, char* key, unsigned int key_len, HashTable* options_ht, zval** data TSRMLS_DC );
int get_conn_option_key( sqlsrv_context& ctx, char* key, unsigned int key_len, zval const* value_z TSRMLS_DC );
int get_stmt_option_key( char* key, unsigned int key_len TSRMLS_DC );
void validate_conn_options( sqlsrv_context& ctx, zval* user_options_z, _Out_ char** uid, _Out_ char** pwd,
_Inout_ HashTable* ss_conn_options_ht TSRMLS_DC );
void validate_stmt_options( sqlsrv_context& ctx, zval* stmt_options, _Inout_ HashTable* ss_stmt_options_ht TSRMLS_DC );
void add_conn_option_key( sqlsrv_context& ctx, zend_string* key, size_t key_len,
HashTable* options_ht, zval* data TSRMLS_DC );
void add_stmt_option_key( sqlsrv_context& ctx, zend_string* key, size_t key_len, HashTable* options_ht, zval* data TSRMLS_DC );
int get_conn_option_key( sqlsrv_context& ctx, zend_string* key, size_t key_len, zval const* value_z TSRMLS_DC );
int get_stmt_option_key( zend_string* key, size_t key_len TSRMLS_DC );
}
@ -202,27 +201,27 @@ const stmt_option SS_STMT_OPTS[] = {
SSStmtOptionNames::QUERY_TIMEOUT,
sizeof( SSStmtOptionNames::QUERY_TIMEOUT ),
SQLSRV_STMT_OPTION_QUERY_TIMEOUT,
new stmt_option_query_timeout
std::unique_ptr<stmt_option_query_timeout>( new stmt_option_query_timeout )
},
{
SSStmtOptionNames::SEND_STREAMS_AT_EXEC,
sizeof( SSStmtOptionNames::SEND_STREAMS_AT_EXEC ),
SQLSRV_STMT_OPTION_SEND_STREAMS_AT_EXEC,
new stmt_option_send_at_exec
std::unique_ptr<stmt_option_send_at_exec>( new stmt_option_send_at_exec )
},
{
SSStmtOptionNames::SCROLLABLE,
sizeof( SSStmtOptionNames::SCROLLABLE ),
SQLSRV_STMT_OPTION_SCROLLABLE,
new stmt_option_scrollable
std::unique_ptr<stmt_option_scrollable>( new stmt_option_scrollable )
},
{
SSStmtOptionNames::CLIENT_BUFFER_MAX_SIZE,
sizeof( SSStmtOptionNames::CLIENT_BUFFER_MAX_SIZE ),
SQLSRV_STMT_OPTION_CLIENT_BUFFER_MAX_SIZE,
new stmt_option_buffered_query_limit
std::unique_ptr<stmt_option_buffered_query_limit>( new stmt_option_buffered_query_limit )
},
{ NULL, 0, SQLSRV_STMT_OPTION_INVALID, NULL },
{ NULL, 0, SQLSRV_STMT_OPTION_INVALID, std::unique_ptr<stmt_option_functor>{} },
};
@ -415,9 +414,6 @@ const connection_option SS_CONN_OPTS[] = {
PHP_FUNCTION ( sqlsrv_connect )
{
SQLSRV_UNUSED( return_value_used );
SQLSRV_UNUSED( this_ptr );
SQLSRV_UNUSED( return_value_ptr );
LOG_FUNCTION( "sqlsrv_connect" );
SET_FUNCTION_NAME( *g_henv_cp );
@ -429,8 +425,9 @@ PHP_FUNCTION ( sqlsrv_connect )
zval* options_z = NULL;
char* uid = NULL;
char* pwd = NULL;
unsigned int server_len = 0;
size_t server_len = 0;
zval conn_z;
ZVAL_UNDEF(&conn_z);
// get the server name and connection options
int result = zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s|a", &server, &server_len, &options_z );
@ -447,7 +444,7 @@ PHP_FUNCTION ( sqlsrv_connect )
// Initialize the options array to be passed to the core layer
ALLOC_HASHTABLE( ss_conn_options_ht );
core::sqlsrv_zend_hash_init( *g_henv_cp, ss_conn_options_ht, 10 /* # of buckets */, NULL /*hashfn*/,
core::sqlsrv_zend_hash_init( *g_henv_cp, ss_conn_options_ht, 10 /* # of buckets */,
ZVAL_PTR_DTOR, 0 /*persistent*/ TSRMLS_CC );
// Either of g_henv_cp or g_henv_ncp can be used to propogate the error.
@ -463,13 +460,15 @@ PHP_FUNCTION ( sqlsrv_connect )
// create a bunch of statements
ALLOC_HASHTABLE( stmts );
core::sqlsrv_zend_hash_init( *g_henv_cp, stmts, 5, NULL /* hashfn */, NULL /* dtor */, 0 /* persistent */ TSRMLS_CC );
core::sqlsrv_zend_hash_init( *g_henv_cp, stmts, 5, NULL /* dtor */, 0 /* persistent */ TSRMLS_CC );
// register the connection with the PHP runtime
ss::zend_register_resource( return_value, conn, ss_sqlsrv_conn::descriptor, ss_sqlsrv_conn::resource_name TSRMLS_CC );
// register the connection with the PHP runtime
ss::zend_register_resource(conn_z, conn, ss_sqlsrv_conn::descriptor, ss_sqlsrv_conn::resource_name TSRMLS_CC);
conn->stmts = stmts;
stmts.transferred();
RETURN_RES( Z_RES(conn_z) );
}
catch( core::CoreException& ) {
@ -515,9 +514,6 @@ PHP_FUNCTION( sqlsrv_begin_transaction )
{
LOG_FUNCTION( "sqlsrv_begin_transaction" );
SQLSRV_UNUSED( return_value_used );
SQLSRV_UNUSED( this_ptr );
SQLSRV_UNUSED( return_value_ptr );
ss_sqlsrv_conn* conn = NULL;
PROCESS_PARAMS( conn, "r", _FN_, 0 );
@ -562,71 +558,64 @@ PHP_FUNCTION( sqlsrv_begin_transaction )
PHP_FUNCTION( sqlsrv_close )
{
SQLSRV_UNUSED( return_value_used );
SQLSRV_UNUSED( this_ptr );
SQLSRV_UNUSED( return_value_ptr );
LOG_FUNCTION( "sqlsrv_close" );
zval* conn_r = NULL;
ss_sqlsrv_conn* conn = NULL;
sqlsrv_context_auto_ptr error_ctx;
full_mem_check(MEMCHECK_SILENT);
reset_errors( TSRMLS_C );
try {
// dummy context to pass to the error handler
// dummy context to pass to the error handler
error_ctx = new (sqlsrv_malloc( sizeof( sqlsrv_context ))) sqlsrv_context( 0, ss_error_handler, NULL );
SET_FUNCTION_NAME( *error_ctx );
if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "r", &conn_r ) == FAILURE ) {
if( zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &conn_r) == FAILURE ) {
// Check if it was a zval
int zr = zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "z", &conn_r );
CHECK_CUSTOM_ERROR(( zr == FAILURE ), error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ) {
throw ss::SSException();
}
// if sqlsrv_close was called on a non-existent connection than we just return success.
// if sqlsrv_close was called on a non-existent connection then we just return success.
if( Z_TYPE_P( conn_r ) == IS_NULL ) {
RETURN_TRUE;
}
else {
THROW_CORE_ERROR( error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ );
else {
THROW_CORE_ERROR( error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ );
}
}
conn = static_cast<ss_sqlsrv_conn*>( zend_fetch_resource( &conn_r TSRMLS_CC, -1, ss_sqlsrv_conn::resource_name, NULL, 1,
ss_sqlsrv_conn::descriptor ));
CHECK_CUSTOM_ERROR(( conn == NULL ), error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ) {
conn = static_cast<ss_sqlsrv_conn*>( zend_fetch_resource( Z_RES_P( conn_r ) TSRMLS_CC, ss_sqlsrv_conn::resource_name, ss_sqlsrv_conn::descriptor ));
// if sqlsrv_close was called on an already closed connection then we just return success.
if ( Z_RES_TYPE_P( conn_r ) == RSRC_INVALID_TYPE) {
RETURN_TRUE;
}
CHECK_CUSTOM_ERROR(( conn == NULL ), error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ) {
throw ss::SSException();
}
SET_FUNCTION_NAME( *conn );
// cause any variables still holding a reference to this to be invalid so they cause
// an error when passed to a sqlsrv function. There's nothing we can do if the
// removal fails, so we just log it and move on.
int zr = zend_hash_index_del( &EG( regular_list ), Z_RESVAL_P( conn_r ));
if( zr == FAILURE ) {
LOG( SEV_ERROR, "Failed to remove connection resource %1!d!", Z_RESVAL_P( conn_r ));
if( zend_list_close( Z_RES_P( conn_r ) ) == FAILURE ) {
LOG( SEV_ERROR, "Failed to remove connection resource %1!d!", Z_RES_HANDLE_P( conn_r ));
}
ZVAL_NULL( conn_r );
ZVAL_NULL( conn_r );
RETURN_TRUE;
}
catch( core::CoreException& ) {
RETURN_FALSE;
}
catch( ... ) {
@ -635,7 +624,7 @@ PHP_FUNCTION( sqlsrv_close )
}
}
void __cdecl sqlsrv_conn_dtor( zend_rsrc_list_entry *rsrc TSRMLS_DC )
void __cdecl sqlsrv_conn_dtor( zend_resource *rsrc TSRMLS_DC )
{
LOG_FUNCTION( "sqlsrv_conn_dtor" );
@ -680,10 +669,6 @@ PHP_FUNCTION( sqlsrv_commit )
{
LOG_FUNCTION( "sqlsrv_commit" );
SQLSRV_UNUSED( return_value_used );
SQLSRV_UNUSED( this_ptr );
SQLSRV_UNUSED( return_value_ptr );
ss_sqlsrv_conn* conn = NULL;
PROCESS_PARAMS( conn, "r", _FN_, 0 );
@ -737,9 +722,6 @@ PHP_FUNCTION( sqlsrv_rollback )
{
LOG_FUNCTION( "sqlsrv_rollback" );
SQLSRV_UNUSED( return_value_used );
SQLSRV_UNUSED( this_ptr );
SQLSRV_UNUSED( return_value_ptr );
ss_sqlsrv_conn* conn = NULL;
@ -773,9 +755,6 @@ PHP_FUNCTION( sqlsrv_rollback )
PHP_FUNCTION( sqlsrv_client_info )
{
SQLSRV_UNUSED( return_value_used );
SQLSRV_UNUSED( this_ptr );
SQLSRV_UNUSED( return_value_ptr );
LOG_FUNCTION( "sqlsrv_client_info" );
ss_sqlsrv_conn* conn = NULL;
@ -818,10 +797,6 @@ PHP_FUNCTION( sqlsrv_server_info )
{
try {
SQLSRV_UNUSED( return_value_used );
SQLSRV_UNUSED( this_ptr );
SQLSRV_UNUSED( return_value_ptr );
LOG_FUNCTION( "sqlsrv_server_info" );
ss_sqlsrv_conn* conn = NULL;
PROCESS_PARAMS( conn, "r", _FN_, 0 );
@ -873,21 +848,18 @@ PHP_FUNCTION( sqlsrv_server_info )
PHP_FUNCTION( sqlsrv_prepare )
{
SQLSRV_UNUSED( return_value_used );
SQLSRV_UNUSED( this_ptr );
SQLSRV_UNUSED( return_value_ptr );
LOG_FUNCTION( "sqlsrv_prepare" );
sqlsrv_malloc_auto_ptr<ss_sqlsrv_stmt> stmt;
ss_sqlsrv_conn* conn = NULL;
char *sql = NULL;
unsigned int sql_len = 0;
zend_long sql_len = 0;
zval* params_z = NULL;
zval* options_z = NULL;
hash_auto_ptr ss_stmt_options_ht;
zval_auto_ptr stmt_z;
ALLOC_INIT_ZVAL( stmt_z );
zval stmt_z;
ZVAL_UNDEF(&stmt_z);
PROCESS_PARAMS( conn, "rs|a!a!", _FN_, 4, &sql, &sql_len, &params_z, &options_z );
@ -897,7 +869,7 @@ PHP_FUNCTION( sqlsrv_prepare )
// Initialize the options array to be passed to the core layer
ALLOC_HASHTABLE( ss_stmt_options_ht );
core::sqlsrv_zend_hash_init( *conn , ss_stmt_options_ht, 3 /* # of buckets */, NULL /*hashfn*/,
core::sqlsrv_zend_hash_init( *conn , ss_stmt_options_ht, 3 /* # of buckets */,
ZVAL_PTR_DTOR, 0 /*persistent*/ TSRMLS_CC );
validate_stmt_options( *conn, options_z, ss_stmt_options_ht TSRMLS_CC );
@ -914,7 +886,7 @@ PHP_FUNCTION( sqlsrv_prepare )
if( sql == NULL ) {
DIE( "sqlsrv_query: sql string was null." );
DIE( "sqlsrv_prepare: sql string was null." );
}
stmt = static_cast<ss_sqlsrv_stmt*>( core_sqlsrv_create_stmt( conn, core::allocate_stmt<ss_sqlsrv_stmt>,
@ -923,7 +895,11 @@ PHP_FUNCTION( sqlsrv_prepare )
core_sqlsrv_prepare( stmt, sql, sql_len TSRMLS_CC );
mark_params_by_reference( stmt, params_z TSRMLS_CC );
//mark_params_by_reference( stmt, params_z TSRMLS_CC );
if (params_z) {
stmt->params_z = (zval *)sqlsrv_malloc(sizeof(zval));
ZVAL_COPY(stmt->params_z, params_z);
}
stmt->prepared = true;
@ -932,19 +908,16 @@ PHP_FUNCTION( sqlsrv_prepare )
// store the resource id with the connection so the connection
// can release this statement when it closes.
int next_index = zend_hash_next_free_element( conn->stmts );
long rsrc_idx = Z_RESVAL_P( stmt_z );
core::sqlsrv_zend_hash_index_update(*conn, conn->stmts, next_index, &rsrc_idx, sizeof( long ) TSRMLS_CC );
zend_long next_index = zend_hash_next_free_element( conn->stmts );
core::sqlsrv_zend_hash_index_update(*conn, conn->stmts, next_index, &stmt_z TSRMLS_CC);
stmt->conn_index = next_index;
// the statement is now registered with EG( regular_list )
stmt.transferred();
zval_ptr_dtor( &return_value );
*return_value_ptr = stmt_z;
stmt_z.transferred();
RETURN_RES(Z_RES(stmt_z));
}
catch( core::CoreException& ) {
@ -954,8 +927,8 @@ PHP_FUNCTION( sqlsrv_prepare )
stmt->conn = NULL;
stmt->~ss_sqlsrv_stmt();
}
if( Z_TYPE_P( stmt_z ) != IS_NULL ) {
free_stmt_resource( stmt_z TSRMLS_CC );
if (!Z_ISUNDEF(stmt_z)) {
free_stmt_resource(&stmt_z TSRMLS_CC);
}
RETURN_FALSE;
@ -998,9 +971,6 @@ PHP_FUNCTION( sqlsrv_prepare )
PHP_FUNCTION( sqlsrv_query )
{
SQLSRV_UNUSED( return_value_used );
SQLSRV_UNUSED( this_ptr );
SQLSRV_UNUSED( return_value_ptr );
LOG_FUNCTION( "sqlsrv_query" );
@ -1010,9 +980,10 @@ PHP_FUNCTION( sqlsrv_query )
hash_auto_ptr ss_stmt_options_ht;
int sql_len = 0;
zval* options_z = NULL;
zval* params_z = NULL;
zval_auto_ptr stmt_z;
zval* params_z = NULL;
zval stmt_z;
ZVAL_UNDEF(&stmt_z);
PROCESS_PARAMS( conn, "rs|a!a!", _FN_, 4, &sql, &sql_len, &params_z, &options_z );
try {
@ -1022,7 +993,7 @@ PHP_FUNCTION( sqlsrv_query )
// Initialize the options array to be passed to the core layer
ALLOC_HASHTABLE( ss_stmt_options_ht );
core::sqlsrv_zend_hash_init( *conn , ss_stmt_options_ht, 3 /* # of buckets */, NULL /*hashfn*/, ZVAL_PTR_DTOR,
core::sqlsrv_zend_hash_init( *conn , ss_stmt_options_ht, 3 /* # of buckets */, ZVAL_PTR_DTOR,
0 /*persistent*/ TSRMLS_CC );
validate_stmt_options( *conn, options_z, ss_stmt_options_ht TSRMLS_CC );
@ -1045,9 +1016,9 @@ PHP_FUNCTION( sqlsrv_query )
ss_stmt_options_ht, SS_STMT_OPTS,
ss_error_handler, NULL TSRMLS_CC ) );
stmt->params_z = params_z;
if( params_z ) {
zval_add_ref( &params_z );
stmt->params_z = (zval *)sqlsrv_malloc(sizeof(zval));
ZVAL_COPY(stmt->params_z, params_z);
}
stmt->set_func( "sqlsrv_query" );
@ -1056,22 +1027,18 @@ PHP_FUNCTION( sqlsrv_query )
// execute the statement
core_sqlsrv_execute( stmt TSRMLS_CC, sql, sql_len );
// register the statement with the PHP runtime
ALLOC_INIT_ZVAL( stmt_z );
ss::zend_register_resource( stmt_z, stmt, ss_sqlsrv_stmt::descriptor, ss_sqlsrv_stmt::resource_name TSRMLS_CC );
// register the statement with the PHP runtime
ss::zend_register_resource(stmt_z, stmt, ss_sqlsrv_stmt::descriptor, ss_sqlsrv_stmt::resource_name TSRMLS_CC);
// store the resource id with the connection so the connection
// can release this statement when it closes.
int next_index = zend_hash_next_free_element( conn->stmts );
long rsrc_idx = Z_RESVAL_P( stmt_z );
core::sqlsrv_zend_hash_index_update(*conn, conn->stmts, next_index, &rsrc_idx, sizeof( long ) TSRMLS_CC );
zend_ulong next_index = zend_hash_next_free_element( conn->stmts );
core::sqlsrv_zend_hash_index_update(*conn, conn->stmts, next_index, &stmt_z TSRMLS_CC);
stmt->conn_index = next_index;
stmt.transferred();
zval_ptr_dtor( &return_value );
*return_value_ptr = stmt_z;
stmt_z.transferred();
RETURN_RES(Z_RES(stmt_z));
}
catch( core::CoreException& ) {
@ -1081,9 +1048,8 @@ PHP_FUNCTION( sqlsrv_query )
stmt->conn = NULL; // tell the statement that it isn't part of the connection so it doesn't try to remove itself
stmt->~ss_sqlsrv_stmt();
}
if( stmt_z ) {
free_stmt_resource( stmt_z TSRMLS_CC );
if (!Z_ISUNDEF(stmt_z)) {
free_stmt_resource(&stmt_z TSRMLS_CC);
}
RETURN_FALSE;
@ -1096,13 +1062,11 @@ PHP_FUNCTION( sqlsrv_query )
void free_stmt_resource( zval* stmt_z TSRMLS_DC )
{
int zr = zend_hash_index_del( &EG( regular_list ), Z_RESVAL_P( stmt_z ));
if( zr == FAILURE ) {
LOG( SEV_ERROR, "Failed to remove stmt resource %1!d!", Z_RESVAL_P( stmt_z ));
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 );
zval_ptr_dtor( &stmt_z );
zval_ptr_dtor(stmt_z);
}
// internal connection functions
@ -1121,58 +1085,52 @@ 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 )) {
long* rsrc_idx_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, reinterpret_cast<void**>( &rsrc_idx_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;
int type = -1;
stmt = static_cast<ss_sqlsrv_stmt*>( zend_list_find( *rsrc_idx_ptr, &type ));
if( stmt == NULL || type != 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.
core::sqlsrv_zend_hash_index_del( *conn, &EG( regular_list ), *rsrc_idx_ptr TSRMLS_CC );
}
catch( core::CoreException& ) {
LOG( SEV_ERROR, "Failed to remove statement resource %1!d! when closing the connection", *rsrc_idx_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 );
conn->stmts = NULL;
}
int get_conn_option_key( sqlsrv_context& ctx, char* key, unsigned int key_len, zval const* value_z TSRMLS_DC )
int get_conn_option_key( sqlsrv_context& ctx, zend_string* key, size_t key_len, zval const* value_z TSRMLS_DC )
{
for( int i=0; SS_CONN_OPTS[ i ].conn_option_key != SQLSRV_CONN_OPTION_INVALID; ++i )
{
if( key_len == SS_CONN_OPTS[ i ].sqlsrv_len && !stricmp( key, SS_CONN_OPTS[ i ].sqlsrv_name )) {
if( key_len == SS_CONN_OPTS[ i ].sqlsrv_len && !stricmp( ZSTR_VAL( key ), SS_CONN_OPTS[ i ].sqlsrv_name )) {
switch( SS_CONN_OPTS[ i ].value_type ) {
@ -1201,7 +1159,7 @@ int get_conn_option_key( sqlsrv_context& ctx, char* key, unsigned int key_len, z
}
char* value = Z_STRVAL_P( value_z );
int value_len = Z_STRLEN_P( value_z );
size_t value_len = Z_STRLEN_P( value_z );
bool escaped = core_is_conn_opt_value_escaped( value, value_len );
CHECK_CUSTOM_ERROR( !escaped, ctx, SS_SQLSRV_ERROR_CONNECT_BRACES_NOT_ESCAPED, SS_CONN_OPTS[ i ].sqlsrv_name ) {
@ -1218,80 +1176,73 @@ int get_conn_option_key( sqlsrv_context& ctx, char* key, unsigned int key_len, z
return SQLSRV_CONN_OPTION_INVALID;
}
int get_stmt_option_key( char* key, unsigned int key_len TSRMLS_DC )
int get_stmt_option_key( zend_string* key, size_t key_len TSRMLS_DC )
{
for( int i = 0; SS_STMT_OPTS[ i ].key != SQLSRV_STMT_OPTION_INVALID; ++i )
{
if( key_len == SS_STMT_OPTS[ i ].name_len && !stricmp( key, SS_STMT_OPTS[ i ].name )) {
if( key_len == SS_STMT_OPTS[ i ].name_len && !stricmp( ZSTR_VAL( key ), SS_STMT_OPTS[ i ].name )) {
return SS_STMT_OPTS[ i ].key;
}
}
return SQLSRV_STMT_OPTION_INVALID;
}
void add_stmt_option_key( sqlsrv_context& ctx, char* key, unsigned int key_len,
HashTable* options_ht, zval** data TSRMLS_DC )
void add_stmt_option_key( sqlsrv_context& ctx, zend_string* key, size_t key_len,
HashTable* options_ht, zval* data TSRMLS_DC )
{
int option_key = ::get_stmt_option_key( key, key_len TSRMLS_CC );
CHECK_CUSTOM_ERROR((option_key == SQLSRV_STMT_OPTION_INVALID ), ctx, SQLSRV_ERROR_INVALID_OPTION_KEY, key ) {
CHECK_CUSTOM_ERROR((option_key == SQLSRV_STMT_OPTION_INVALID ), ctx, SQLSRV_ERROR_INVALID_OPTION_KEY, ZSTR_VAL( key ) ) {
throw ss::SSException();
}
zval_add_ref( data ); // inc the ref count since this is going into the options_ht too.
core::sqlsrv_zend_hash_index_update( ctx, options_ht, option_key, (void**)data, sizeof(zval*) TSRMLS_CC );
Z_TRY_ADDREF_P(data); // inc the ref count since this is going into the options_ht too.
core::sqlsrv_zend_hash_index_update( ctx, options_ht, option_key, data TSRMLS_CC );
}
void add_conn_option_key( sqlsrv_context& ctx, char* key, unsigned int key_len,
HashTable* options_ht, zval** data TSRMLS_DC )
void add_conn_option_key( sqlsrv_context& ctx, zend_string* key, size_t key_len,
HashTable* options_ht, zval* data TSRMLS_DC )
{
int option_key = ::get_conn_option_key( ctx, key, key_len, *data TSRMLS_CC );
int option_key = ::get_conn_option_key( ctx, key, key_len, data TSRMLS_CC );
CHECK_CUSTOM_ERROR((option_key == SQLSRV_STMT_OPTION_INVALID ), ctx, SS_SQLSRV_ERROR_INVALID_OPTION, key ) {
throw ss::SSException();
}
zval_add_ref( data ); // inc the ref count since this is going into the options_ht too.
core::sqlsrv_zend_hash_index_update( ctx, options_ht, option_key, (void**)data, sizeof(zval*) TSRMLS_CC );
Z_TRY_ADDREF_P(data); // inc the ref count since this is going into the options_ht too.
core::sqlsrv_zend_hash_index_update( ctx, options_ht, option_key, data TSRMLS_CC );
}
// Iterates through the list of statement options provided by the user and validates them
// against the list of supported statement options by this driver. After validation
// creates a Hashtable of statement options to be sent to the core layer for processing.
void validate_stmt_options( sqlsrv_context& ctx, zval* stmt_options, __inout HashTable* ss_stmt_options_ht TSRMLS_DC )
void validate_stmt_options( sqlsrv_context& ctx, zval* stmt_options, _Inout_ HashTable* ss_stmt_options_ht TSRMLS_DC )
{
try {
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_EXISTANT;
char *key = NULL;
unsigned int key_len = 0;
unsigned long int_key = -1;
zval** data;
zval* conn_opt = NULL;
int result = 0;
type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG;
type = zend_hash_get_current_key_ex( options_ht, &key, &key_len, &int_key, 0, NULL );
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, (void**) &data TSRMLS_CC );
add_stmt_option_key( ctx, key, key_len, ss_stmt_options_ht, data TSRMLS_CC );
}
if (type != HASH_KEY_IS_STRING) {
CHECK_CUSTOM_ERROR(true, ctx, SQLSRV_ERROR_INVALID_OPTION_KEY, std::to_string( int_key ).c_str() ) {
throw core::CoreException();
}
}
key_len = ZSTR_LEN(key) + 1;
add_stmt_option_key( ctx, key, key_len, ss_stmt_options_ht, data TSRMLS_CC );
} ZEND_HASH_FOREACH_END();
}
}
catch( core::CoreException& ) {
@ -1304,45 +1255,44 @@ void validate_stmt_options( sqlsrv_context& ctx, zval* stmt_options, __inout Has
// against the predefined list of supported connection options by this driver. After validation
// creates a Hashtable of connection options to be sent to the core layer for processing.
void validate_conn_options( sqlsrv_context& ctx, zval* user_options_z, __out char** uid, __out char** pwd, __inout HashTable* ss_conn_options_ht TSRMLS_DC )
void validate_conn_options( sqlsrv_context& ctx, zval* user_options_z, _Out_ char** uid, _Out_ char** pwd, _Inout_ HashTable* ss_conn_options_ht TSRMLS_DC )
{
try {
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_EXISTANT;
char *key = NULL;
unsigned int key_len = 0;
unsigned long 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_ex( options_ht, &key, &key_len, &int_key, 0, NULL );
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, (void**) &data TSRMLS_CC );
if( key_len == sizeof( SSConnOptionNames::UID ) && !stricmp( key, SSConnOptionNames::UID )) {
CHECK_CUSTOM_ERROR(( type != HASH_KEY_IS_STRING ), ctx, SS_SQLSRV_ERROR_INVALID_CONNECTION_KEY ) {
throw ss::SSException();
}
*uid = Z_STRVAL_PP( data );
}
// 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 )) {
else if( key_len == sizeof( SSConnOptionNames::PWD ) && !stricmp( key, SSConnOptionNames::PWD )) {
*pwd = Z_STRVAL_PP( data );
}
else {
*uid = Z_STRVAL_P( data );
}
::add_conn_option_key( ctx, key, key_len, ss_conn_options_ht, data TSRMLS_CC );
}
}
else if( key_len == sizeof( SSConnOptionNames::PWD ) && !stricmp( ZSTR_VAL( key ), SSConnOptionNames::PWD )) {
*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

@ -3,7 +3,7 @@
//
// Contents: Core routines that use connection handles shared between sqlsrv and pdo_sqlsrv
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -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}";
@ -57,12 +57,12 @@ const char CONNECTION_OPTION_MARS_ON[] = "MARS_Connection={Yes};";
void build_connection_string_and_set_conn_attr( sqlsrv_conn* conn, const char* server, const char* uid, const char* pwd,
HashTable* options_ht, const connection_option valid_conn_opts[],
void* driver,__inout std::string& connection_string TSRMLS_DC );
void* driver,_Inout_ std::string& connection_string TSRMLS_DC );
void determine_server_version( sqlsrv_conn* conn TSRMLS_DC );
const char* get_processor_arch( void );
void get_server_version( sqlsrv_conn* conn, char** server_version, SQLSMALLINT& len TSRMLS_DC );
connection_option const* get_connection_option( sqlsrv_conn* conn, const char* key, unsigned int key_len TSRMLS_DC );
void common_conn_str_append_func( const char* odbc_name, const char* val, int val_len, std::string& conn_str TSRMLS_DC );
connection_option const* get_connection_option( sqlsrv_conn* conn, const char* key, SQLULEN key_len TSRMLS_DC );
void common_conn_str_append_func( const char* odbc_name, const char* val, size_t val_len, std::string& conn_str TSRMLS_DC );
}
@ -104,14 +104,14 @@ sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_
if( options_ht && zend_hash_num_elements( options_ht ) > 0 ) {
zval** option_zz = NULL;
zval* option_z = NULL;
int zr = SUCCESS;
zr = zend_hash_index_find( options_ht, SQLSRV_CONN_OPTION_CONN_POOLING, reinterpret_cast<void**>( &option_zz ));
if( zr != FAILURE ) {
option_z = zend_hash_index_find(options_ht, SQLSRV_CONN_OPTION_CONN_POOLING);
if (option_z) {
// if the option was found and it's not true, then use the non pooled environment handle
if(( Z_TYPE_PP( option_zz ) == IS_STRING && !core_str_zval_is_true( *option_zz )) || !zend_is_true( *option_zz ) ) {
if(( Z_TYPE_P( option_z ) == IS_STRING && !core_str_zval_is_true( option_z )) || !zend_is_true( option_z ) ) {
henv = &henv_ncp;
}
@ -124,38 +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 = (conn_str.length() + 1) * sizeof( wchar_t );
wconn_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_UTF8, conn_str.c_str(), 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();
// 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 );
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();
}
}
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();
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();
}
@ -167,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() );
@ -318,7 +329,7 @@ void core_sqlsrv_close( sqlsrv_conn* conn TSRMLS_DC )
// sql - T-SQL command to prepare
// sql_len - length of the T-SQL string
void core_sqlsrv_prepare( sqlsrv_stmt* stmt, const char* sql, long sql_len TSRMLS_DC )
void core_sqlsrv_prepare( sqlsrv_stmt* stmt, const char* sql, SQLLEN sql_len TSRMLS_DC )
{
try {
@ -333,10 +344,17 @@ void core_sqlsrv_prepare( sqlsrv_stmt* stmt, const char* sql, long sql_len TSRML
wsql_len = 0;
}
else {
if (sql_len > INT_MAX)
{
LOG(SEV_ERROR, "Convert input parameter to utf16: buffer length exceeded.");
throw core::CoreException();
}
SQLSRV_ENCODING encoding = (( stmt->encoding() == SQLSRV_ENCODING_DEFAULT ) ? stmt->conn->encoding() :
stmt->encoding() );
wsql_string = utf16_string_from_mbcs_string( encoding, reinterpret_cast<const char*>( sql ),
sql_len, &wsql_len );
static_cast<int>( sql_len ), &wsql_len );
CHECK_CUSTOM_ERROR( wsql_string == NULL, stmt, SQLSRV_ERROR_QUERY_STRING_ENCODING_TRANSLATE,
get_last_error_message() ) {
throw core::CoreException();
@ -359,7 +377,7 @@ void core_sqlsrv_prepare( sqlsrv_stmt* stmt, const char* sql, long sql_len TSRML
// conn - The connection resource by which the client and server are connected.
// *server_version - zval for returning results.
void core_sqlsrv_get_server_version( sqlsrv_conn* conn, __out zval *server_version TSRMLS_DC )
void core_sqlsrv_get_server_version( sqlsrv_conn* conn, _Out_ zval *server_version TSRMLS_DC )
{
try {
@ -367,7 +385,10 @@ void core_sqlsrv_get_server_version( sqlsrv_conn* conn, __out zval *server_versi
SQLSMALLINT buffer_len = 0;
get_server_version( conn, &buffer, buffer_len TSRMLS_CC );
ZVAL_STRINGL( server_version, buffer, buffer_len, 0 );
core::sqlsrv_zval_stringl( server_version, buffer, buffer_len );
if (NULL != buffer) {
sqlsrv_free( buffer );
}
buffer.transferred();
}
@ -383,7 +404,7 @@ void core_sqlsrv_get_server_version( sqlsrv_conn* conn, __out zval *server_versi
// conn - The connection resource by which the client and server are connected.
// *server_info - zval for returning results.
void core_sqlsrv_get_server_info( sqlsrv_conn* conn, __out zval *server_info TSRMLS_DC )
void core_sqlsrv_get_server_info( sqlsrv_conn* conn, _Out_ zval *server_info TSRMLS_DC )
{
try {
@ -422,7 +443,7 @@ void core_sqlsrv_get_server_info( sqlsrv_conn* conn, __out zval *server_info TSR
// conn - The connection resource by which the client and server are connected.
// *client_info - zval for returning the results.
void core_sqlsrv_get_client_info( sqlsrv_conn* conn, __out zval *client_info TSRMLS_DC )
void core_sqlsrv_get_client_info( sqlsrv_conn* conn, _Out_ zval *client_info TSRMLS_DC )
{
try {
@ -463,7 +484,7 @@ void core_sqlsrv_get_client_info( sqlsrv_conn* conn, __out zval *client_info TSR
// Properly escaped means that any '}' should be escaped by a prior '}'. It is assumed that
// the value will be surrounded by { and } by the caller after it has been validated
bool core_is_conn_opt_value_escaped( const char* value, int value_len )
bool core_is_conn_opt_value_escaped( const char* value, size_t value_len )
{
// if the value is already quoted, then only analyse the part inside the quotes and return it as
// unquoted since we quote it when adding it to the connection string.
@ -472,7 +493,7 @@ bool core_is_conn_opt_value_escaped( const char* value, int value_len )
value_len -= 2;
}
// check to make sure that all right braces are escaped
int i = 0;
size_t i = 0;
while( ( value[i] != '}' || ( value[i] == '}' && value[i+1] == '}' )) && i < value_len ) {
// skip both braces
if( value[i] == '}' )
@ -491,7 +512,7 @@ bool core_is_conn_opt_value_escaped( const char* value, int value_len )
namespace {
connection_option const* get_connection_option( sqlsrv_conn* conn, unsigned long key,
connection_option const* get_connection_option( sqlsrv_conn* conn, SQLULEN key,
const connection_option conn_opts[] TSRMLS_DC )
{
for( int opt_idx = 0; conn_opts[ opt_idx ].conn_option_key != SQLSRV_CONN_OPTION_INVALID; ++opt_idx ) {
@ -513,7 +534,7 @@ connection_option const* get_connection_option( sqlsrv_conn* conn, unsigned long
void build_connection_string_and_set_conn_attr( sqlsrv_conn* conn, const char* server, const char* uid, const char* pwd,
HashTable* options, const connection_option valid_conn_opts[],
void* driver,__inout std::string& connection_string TSRMLS_DC )
void* driver,_Inout_ std::string& connection_string TSRMLS_DC )
{
bool credentials_mentioned = false;
bool mars_mentioned = false;
@ -521,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 );
@ -566,40 +585,34 @@ void build_connection_string_and_set_conn_attr( sqlsrv_conn* conn, const char* s
// flag is set to false.
if( zend_hash_index_exists( options, SQLSRV_CONN_OPTION_TRACE_FILE )) {
zval** trace_value = NULL;
int zr = zend_hash_index_find( options, SQLSRV_CONN_OPTION_TRACE_ON, (void**)&trace_value );
zval* trace_value = NULL;
trace_value = zend_hash_index_find(options, SQLSRV_CONN_OPTION_TRACE_ON);
if( zr == FAILURE || !zend_is_true( *trace_value )) {
if (trace_value == NULL || !zend_is_true(trace_value)) {
zend_hash_index_del( options, SQLSRV_CONN_OPTION_TRACE_FILE );
}
}
for( zend_hash_internal_pointer_reset( options );
zend_hash_has_more_elements( options ) == SUCCESS;
zend_hash_move_forward( options )) {
int type = HASH_KEY_NON_EXISTANT;
char *key = NULL;
unsigned int key_len = -1;
unsigned long index = -1;
zval** data = NULL;
zend_string *key = NULL;
zend_ulong index = -1;
zval* data = NULL;
type = zend_hash_get_current_key_ex( options, &key, &key_len, &index, 0, NULL );
// 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, (void**) &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 ) {
@ -676,7 +689,7 @@ void determine_server_version( sqlsrv_conn* conn TSRMLS_DC )
errno = 0;
char version_major_str[ 3 ];
SERVER_VERSION version_major;
memcpy( version_major_str, p, 2 );
memcpy_s( version_major_str, sizeof( version_major_str ), p, 2 );
version_major_str[ 2 ] = '\0';
version_major = static_cast<SERVER_VERSION>( atoi( version_major_str ));
@ -690,7 +703,7 @@ void determine_server_version( sqlsrv_conn* conn TSRMLS_DC )
conn->server_version = version_major;
}
void common_conn_str_append_func( const char* odbc_name, const char* val, int val_len, std::string& conn_str TSRMLS_DC )
void common_conn_str_append_func( const char* odbc_name, const char* val, size_t val_len, std::string& conn_str TSRMLS_DC )
{
// wrap a connection option in a quote. It is presumed that any character that need to be escaped will
// be escaped, such as a closing }.
@ -713,7 +726,7 @@ void conn_str_append_func::func( connection_option const* option, zval* value, s
TSRMLS_DC )
{
const char* val_str = Z_STRVAL_P( value );
int val_len = Z_STRLEN_P( value );
size_t val_len = Z_STRLEN_P( value );
common_conn_str_append_func( option->odbc_name, val_str, val_len, conn_str TSRMLS_CC );
}
@ -729,15 +742,15 @@ void conn_null_func::func( connection_option const* /*option*/, zval* /*value*/,
// Values = ("true" or "1") are treated as true values. Everything else is treated as false.
// Returns 1 for true and 0 for false.
int core_str_zval_is_true( zval* value_z )
size_t core_str_zval_is_true( zval* value_z )
{
SQLSRV_ASSERT( Z_TYPE_P( value_z ) == IS_STRING, "core_str_zval_is_true: This function only accepts zval of type string." );
char* value_in = Z_STRVAL_P( value_z );
int val_len = Z_STRLEN_P( value_z );
size_t val_len = Z_STRLEN_P( value_z );
// strip any whitespace at the end (whitespace is the same value in ASCII and UTF-8)
int last_char = val_len - 1;
size_t last_char = val_len - 1;
while( isspace( value_in[ last_char ] )) {
value_in[ last_char ] = '\0';
val_len = last_char;
@ -745,7 +758,7 @@ int core_str_zval_is_true( zval* value_z )
}
// save adjustments to the value made by stripping whitespace at the end
ZVAL_STRINGL( value_z, value_in, val_len, 0 );
Z_STRLEN_P( value_z ) = val_len;
const char VALID_TRUE_VALUE_1[] = "true";
const char VALID_TRUE_VALUE_2[] = "1";

View file

@ -3,7 +3,7 @@
//
// Contents: common initialization routines shared by PDO and sqlsrv
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -37,8 +37,8 @@ OSVERSIONINFO g_osversion;
// err - Driver specific error handler which handles any errors during initialization.
void core_sqlsrv_minit( sqlsrv_context** henv_cp, sqlsrv_context** henv_ncp, error_callback err, const char* driver_func TSRMLS_DC )
{
SQLSRV_STATIC_ASSERT( sizeof( sqlsrv_sqltype ) == sizeof( long ));
SQLSRV_STATIC_ASSERT( sizeof( sqlsrv_phptype ) == sizeof( long ));
SQLSRV_STATIC_ASSERT( sizeof( sqlsrv_sqltype ) == sizeof( zend_long ) );
SQLSRV_STATIC_ASSERT( sizeof( sqlsrv_phptype ) == sizeof( zend_long ));
*henv_cp = *henv_ncp = SQL_NULL_HANDLE; // initialize return values to NULL
@ -146,11 +146,13 @@ void core_sqlsrv_mshutdown( sqlsrv_context& henv_cp, sqlsrv_context& henv_ncp )
henv_ncp.invalidate();
}
delete &henv_ncp;
if( henv_cp != SQL_NULL_HANDLE ) {
henv_cp.invalidate();
}
delete &henv_cp;
return;
}

View file

@ -3,7 +3,7 @@
//
// Contents: Result sets
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -76,19 +76,37 @@ bool get_bit( void* ptr, unsigned int bit )
// read in LOB field during buffered result creation
SQLPOINTER read_lob_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_buffered_result_set::meta_data& meta,
unsigned long mem_used TSRMLS_DC );
zend_long mem_used TSRMLS_DC );
// dtor for each row in the cache
void cache_row_dtor( void* data );
void cache_row_dtor(zval* data);
// convert a number to a string using locales
// There is an extra copy here, but given the size is short (usually <20 bytes) and the complications of
// subclassing a new streambuf just to avoid the copy, it's easier to do the copy
template <typename Char, typename Number>
SQLRETURN number_to_string( Number* number_data, __out void* buffer, SQLLEN buffer_length, __out SQLLEN* out_buffer_length,
SQLRETURN number_to_string( Number* number_data, _Out_ void* buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length,
sqlsrv_error_auto_ptr& last_error )
{
// get to display size by removing the null terminator from buffer length
size_t display_size = ( buffer_length - sizeof( Char )) / sizeof( Char );
std::basic_ostringstream<Char> os;
// use the display size to determine the sql type. And if it is a double, set the precision accordingly
// the display sizes are set by the ODBC driver based on the precision of the sql type
// otherwise we can just use the default precision as long will not be truncated
size_t real_display_size = 14;
size_t float_display_size = 24;
size_t real_precision = 7;
size_t float_precision = 15;
// this is the case of sql type float(24) or real
if ( display_size == real_display_size ) {
os.precision( real_precision );
}
// this is the case of sql type float(53)
else if ( display_size == float_display_size ) {
os.precision( float_precision );
}
std::locale loc;
os.imbue( loc );
std::use_facet< std::num_put< Char > >( loc ).put( std::basic_ostream<Char>::_Iter( os.rdbuf() ), os, ' ', *number_data );
@ -100,21 +118,21 @@ SQLRETURN number_to_string( Number* number_data, __out void* buffer, SQLLEN buff
return SQL_ERROR;
}
if( str_num.size() * sizeof(Char) + sizeof(Char) > (size_t) buffer_length ) {
if( str_num.size() * sizeof(Char) > (size_t) buffer_length ) {
last_error = new ( sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error(
(SQLCHAR*) "HY090", (SQLCHAR*) "Buffer length too small to hold number as string", -1 );
return SQL_ERROR;
}
*out_buffer_length = str_num.size() * sizeof(Char) + sizeof(Char); // include NULL terminator
memcpy( buffer, str_num.c_str(), *out_buffer_length );
*out_buffer_length = str_num.size() * sizeof( Char ); // str_num.size() already include the NULL terminator
memcpy_s( buffer, buffer_length, str_num.c_str(), *out_buffer_length );
return SQL_SUCCESS;
}
template <typename Number, typename Char>
SQLRETURN string_to_number( Char* string_data, SQLLEN str_len, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length, sqlsrv_error_auto_ptr& last_error )
SQLRETURN string_to_number( Char* string_data, SQLLEN str_len, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length, sqlsrv_error_auto_ptr& last_error )
{
Number* number_data = reinterpret_cast<Number*>( buffer );
std::locale loc; // default locale should match system
@ -170,13 +188,13 @@ sqlsrv_error* odbc_get_diag_rec( sqlsrv_stmt* odbc, SQLSMALLINT record_number )
// convert the error into the encoding of the context
sqlsrv_malloc_auto_ptr<SQLCHAR> sql_state;
SQLINTEGER sql_state_len = 0;
SQLLEN sql_state_len = 0;
if (!convert_string_from_utf16( enc, wsql_state, sizeof(wsql_state), (char**)&sql_state, sql_state_len )) {
return NULL;
}
sqlsrv_malloc_auto_ptr<SQLCHAR> native_message;
SQLINTEGER native_message_len = 0;
SQLLEN native_message_len = 0;
if (!convert_string_from_utf16( enc, wnative_message, wnative_message_len, (char**)&native_message, native_message_len )) {
return NULL;
}
@ -214,7 +232,7 @@ SQLRETURN sqlsrv_odbc_result_set::fetch( SQLSMALLINT orientation, SQLLEN offset
}
SQLRETURN sqlsrv_odbc_result_set::get_data( SQLUSMALLINT field_index, SQLSMALLINT target_type,
__out SQLPOINTER buffer, SQLLEN buffer_length, __out SQLLEN* out_buffer_length,
_Out_ SQLPOINTER buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length,
bool handle_warning TSRMLS_DC )
{
SQLSRV_ASSERT( odbc != NULL, "Invalid statement handle" );
@ -222,8 +240,8 @@ SQLRETURN sqlsrv_odbc_result_set::get_data( SQLUSMALLINT field_index, SQLSMALLIN
}
SQLRETURN sqlsrv_odbc_result_set::get_diag_field( SQLSMALLINT record_number, SQLSMALLINT diag_identifier,
__out SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
__out SQLSMALLINT* out_buffer_length TSRMLS_DC )
_Out_ SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
_Out_ SQLSMALLINT* out_buffer_length TSRMLS_DC )
{
SQLSRV_ASSERT( odbc != NULL, "Invalid statement handle" );
return core::SQLGetDiagField( odbc, record_number, diag_identifier, diag_info_buffer, buffer_length,
@ -257,9 +275,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( sqlsrv_stmt* stmt TSRMLS
{
// 10 is an arbitrary number for now for the initial size of the cache
ALLOC_HASHTABLE( cache );
core::sqlsrv_zend_hash_init( *stmt, cache, 10 /* # of buckets */, NULL /* hashfn */, cache_row_dtor /*dtor*/, 0 /*persistent*/
TSRMLS_CC );
core::sqlsrv_zend_hash_init( *stmt, cache, 10 /* # of buckets */, cache_row_dtor /*dtor*/, 0 /*persistent*/ TSRMLS_CC );
col_count = core::SQLNumResultCols( stmt TSRMLS_CC );
// there is no result set to buffer
if( col_count == 0 ) {
@ -441,7 +457,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( sqlsrv_stmt* stmt TSRMLS
// read the data into the cache
// (offset from the above loop has the size of the row buffer necessary)
unsigned long mem_used = 0;
zend_long mem_used = 0;
unsigned long row_count = 0;
while( core::SQLFetchScroll( stmt, SQL_FETCH_NEXT, 0 TSRMLS_CC ) != SQL_NO_DATA ) {
@ -478,7 +494,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( sqlsrv_stmt* stmt TSRMLS
}
else {
mem_used += meta[i].length;
mem_used += meta[i].length;
CHECK_CUSTOM_ERROR( mem_used > stmt->buffered_query_limit * 1024, stmt,
SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED, stmt->buffered_query_limit ) {
@ -523,7 +539,7 @@ sqlsrv_buffered_result_set::sqlsrv_buffered_result_set( sqlsrv_stmt* stmt TSRMLS
// add it to the cache
row_dtor_closure cl( this, row );
sqlsrv_zend_hash_next_index_insert( *stmt, cache, &cl, sizeof( cl ) TSRMLS_CC );
sqlsrv_zend_hash_next_index_insert_mem( *stmt, cache, &cl, sizeof(row_dtor_closure) TSRMLS_CC );
}
}
@ -598,7 +614,7 @@ SQLRETURN sqlsrv_buffered_result_set::fetch( SQLSMALLINT orientation, SQLLEN off
}
SQLRETURN sqlsrv_buffered_result_set::get_data( SQLUSMALLINT field_index, SQLSMALLINT target_type,
__out SQLPOINTER buffer, SQLLEN buffer_length, __out SQLLEN* out_buffer_length,
_Out_ SQLPOINTER buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length,
bool handle_warning TSRMLS_DC )
{
last_error = NULL;
@ -634,8 +650,8 @@ SQLRETURN sqlsrv_buffered_result_set::get_data( SQLUSMALLINT field_index, SQLSMA
}
SQLRETURN sqlsrv_buffered_result_set::get_diag_field( SQLSMALLINT record_number, SQLSMALLINT diag_identifier,
__out SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
__out SQLSMALLINT* out_buffer_length TSRMLS_DC )
_Out_ SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
_Out_ SQLSMALLINT* out_buffer_length TSRMLS_DC )
{
SQLSRV_ASSERT( record_number == 1, "Only record number 1 can be fetched by sqlsrv_buffered_result_set::get_diag_field" );
SQLSRV_ASSERT( diag_identifier == SQL_DIAG_SQLSTATE,
@ -650,7 +666,7 @@ SQLRETURN sqlsrv_buffered_result_set::get_diag_field( SQLSMALLINT record_number,
SQLSRV_ASSERT( last_error->sqlstate != NULL,
"Must have a SQLSTATE in a valid last_error in sqlsrv_buffered_result_set::get_diag_field" );
memcpy( diag_info_buffer, last_error->sqlstate, min( buffer_length, SQL_SQLSTATE_BUFSIZE ));
memcpy_s( diag_info_buffer, buffer_length, last_error->sqlstate, min( buffer_length, SQL_SQLSTATE_BUFSIZE ));
return SQL_SUCCESS;
}
@ -658,8 +674,8 @@ SQLRETURN sqlsrv_buffered_result_set::get_diag_field( SQLSMALLINT record_number,
unsigned char* sqlsrv_buffered_result_set::get_row( void )
{
row_dtor_closure* cl_ptr;
int zr = zend_hash_index_find( cache, current - 1, (void**) &cl_ptr );
SQLSRV_ASSERT( zr == SUCCESS, "Failed to find row %1!d! in the cache", current );
cl_ptr = reinterpret_cast<row_dtor_closure*>(zend_hash_index_find_ptr(cache, static_cast<zend_ulong>(current - 1)));
SQLSRV_ASSERT(cl_ptr != NULL, "Failed to find row %1!d! in the cache", current);
return cl_ptr->row_data;
}
@ -686,8 +702,8 @@ SQLLEN sqlsrv_buffered_result_set::row_count( TSRMLS_D )
// private functions
template <typename Char>
SQLRETURN binary_to_string( SQLCHAR* field_data, SQLLEN& read_so_far, __out void* buffer,
SQLLEN buffer_length, __out SQLLEN* out_buffer_length,
SQLRETURN binary_to_string( SQLCHAR* field_data, SQLLEN& read_so_far, _Out_ void* buffer,
SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length,
sqlsrv_error_auto_ptr& out_error )
{
// hex characters for the conversion loop below
@ -747,8 +763,8 @@ SQLRETURN binary_to_string( SQLCHAR* field_data, SQLLEN& read_so_far, __out voi
return r;
}
SQLRETURN sqlsrv_buffered_result_set::binary_to_system_string( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::binary_to_system_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLCHAR* row = get_row();
SQLCHAR* field_data = NULL;
@ -765,8 +781,8 @@ SQLRETURN sqlsrv_buffered_result_set::binary_to_system_string( SQLSMALLINT field
return binary_to_string<char>( field_data, read_so_far, buffer, buffer_length, out_buffer_length, last_error );
}
SQLRETURN sqlsrv_buffered_result_set::binary_to_wide_string( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::binary_to_wide_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLCHAR* row = get_row();
SQLCHAR* field_data = NULL;
@ -784,11 +800,11 @@ SQLRETURN sqlsrv_buffered_result_set::binary_to_wide_string( SQLSMALLINT field_i
}
SQLRETURN sqlsrv_buffered_result_set::double_to_long( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::double_to_long( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_DOUBLE, "Invalid conversion to long" );
SQLSRV_ASSERT( buffer_length >= sizeof(LONG), "Buffer length must be able to find a long in "
SQLSRV_ASSERT( buffer_length >= sizeof(SQLLEN), "Buffer length must be able to find a long in "
"sqlsrv_buffered_result_set::double_to_long" );
unsigned char* row = get_row();
@ -813,8 +829,8 @@ SQLRETURN sqlsrv_buffered_result_set::double_to_long( SQLSMALLINT field_index, _
return SQL_SUCCESS;
}
SQLRETURN sqlsrv_buffered_result_set::double_to_system_string( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::double_to_system_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_DOUBLE, "Invalid conversion to system string" );
SQLSRV_ASSERT( buffer_length > 0, "Buffer length must be > 0 in sqlsrv_buffered_result_set::double_to_system_string" );
@ -825,8 +841,8 @@ SQLRETURN sqlsrv_buffered_result_set::double_to_system_string( SQLSMALLINT field
return number_to_string<char>( double_data, buffer, buffer_length, out_buffer_length, last_error );
}
SQLRETURN sqlsrv_buffered_result_set::double_to_wide_string( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::double_to_wide_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_DOUBLE, "Invalid conversion to wide string" );
SQLSRV_ASSERT( buffer_length > 0, "Buffer length must be > 0 in sqlsrv_buffered_result_set::double_to_wide_string" );
@ -837,8 +853,8 @@ SQLRETURN sqlsrv_buffered_result_set::double_to_wide_string( SQLSMALLINT field_i
return number_to_string<WCHAR>( double_data, buffer, buffer_length, out_buffer_length, last_error );
}
SQLRETURN sqlsrv_buffered_result_set::long_to_double( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::long_to_double( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_LONG, "Invalid conversion to long" );
SQLSRV_ASSERT( buffer_length >= sizeof(double), "Buffer length must be able to find a long in sqlsrv_buffered_result_set::double_to_long" );
@ -852,8 +868,8 @@ SQLRETURN sqlsrv_buffered_result_set::long_to_double( SQLSMALLINT field_index, _
return SQL_SUCCESS;
}
SQLRETURN sqlsrv_buffered_result_set::long_to_system_string( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::long_to_system_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_LONG, "Invalid conversion to system string" );
SQLSRV_ASSERT( buffer_length > 0, "Buffer length must be > 0 in sqlsrv_buffered_result_set::long_to_system_string" );
@ -864,8 +880,8 @@ SQLRETURN sqlsrv_buffered_result_set::long_to_system_string( SQLSMALLINT field_i
return number_to_string<char>( long_data, buffer, buffer_length, out_buffer_length, last_error );
}
SQLRETURN sqlsrv_buffered_result_set::long_to_wide_string( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::long_to_wide_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_LONG, "Invalid conversion to wide string" );
SQLSRV_ASSERT( buffer_length > 0, "Buffer length must be > 0 in sqlsrv_buffered_result_set::long_to_wide_string" );
@ -876,8 +892,8 @@ SQLRETURN sqlsrv_buffered_result_set::long_to_wide_string( SQLSMALLINT field_ind
return number_to_string<WCHAR>( long_data, buffer, buffer_length, out_buffer_length, last_error );
}
SQLRETURN sqlsrv_buffered_result_set::string_to_double( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::string_to_double( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_CHAR, "Invalid conversion from string to double" );
SQLSRV_ASSERT( buffer_length >= sizeof( double ), "Buffer needs to be big enough to hold a double" );
@ -888,8 +904,8 @@ SQLRETURN sqlsrv_buffered_result_set::string_to_double( SQLSMALLINT field_index,
return string_to_number<double>( string_data, meta[ field_index ].length, buffer, buffer_length, out_buffer_length, last_error );
}
SQLRETURN sqlsrv_buffered_result_set::wstring_to_double( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::wstring_to_double( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_WCHAR, "Invalid conversion from wide string to double" );
SQLSRV_ASSERT( buffer_length >= sizeof( double ), "Buffer needs to be big enough to hold a double" );
@ -900,8 +916,8 @@ SQLRETURN sqlsrv_buffered_result_set::wstring_to_double( SQLSMALLINT field_index
return string_to_number<double>( string_data, meta[ field_index ].length, buffer, buffer_length, out_buffer_length, last_error );
}
SQLRETURN sqlsrv_buffered_result_set::string_to_long( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::string_to_long( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_CHAR, "Invalid conversion from string to long" );
SQLSRV_ASSERT( buffer_length >= sizeof( LONG ), "Buffer needs to be big enough to hold a long" );
@ -912,8 +928,8 @@ SQLRETURN sqlsrv_buffered_result_set::string_to_long( SQLSMALLINT field_index, _
return string_to_number<LONG>( string_data, meta[ field_index ].length, buffer, buffer_length, out_buffer_length, last_error );
}
SQLRETURN sqlsrv_buffered_result_set::wstring_to_long( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::wstring_to_long( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_WCHAR, "Invalid conversion from wide string to long" );
SQLSRV_ASSERT( buffer_length >= sizeof( LONG ), "Buffer needs to be big enough to hold a long" );
@ -924,8 +940,8 @@ SQLRETURN sqlsrv_buffered_result_set::wstring_to_long( SQLSMALLINT field_index,
return string_to_number<LONG>( string_data, meta[ field_index ].length, buffer, buffer_length, out_buffer_length, last_error );
}
SQLRETURN sqlsrv_buffered_result_set::system_to_wide_string( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::system_to_wide_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( last_error == NULL, "Pending error for sqlsrv_buffered_results_set::system_to_wide_string" );
SQLSRV_ASSERT( buffer_length % 2 == 0, "Odd buffer length passed to sqlsrv_buffered_result_set::system_to_wide_string" );
@ -974,8 +990,13 @@ SQLRETURN sqlsrv_buffered_result_set::system_to_wide_string( SQLSMALLINT field_i
bool tried_again = false;
do {
int ch_space = MultiByteToWideChar( CP_ACP, MB_ERR_INVALID_CHARS, (LPCSTR) field_data, to_copy,
(LPWSTR) buffer, to_copy );
if (to_copy > INT_MAX ) {
LOG(SEV_ERROR, "MultiByteToWideChar: Buffer length exceeded.");
throw core::CoreException();
}
int ch_space = MultiByteToWideChar( CP_ACP, MB_ERR_INVALID_CHARS, (LPCSTR) field_data, static_cast<int>(to_copy),
static_cast<LPWSTR>(buffer), static_cast<int>(to_copy));
if( ch_space == 0 ) {
switch( GetLastError() ) {
@ -1012,8 +1033,8 @@ SQLRETURN sqlsrv_buffered_result_set::system_to_wide_string( SQLSMALLINT field_i
return r;
}
SQLRETURN sqlsrv_buffered_result_set::to_same_string( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::to_same_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( last_error == NULL, "Pending error for sqlsrv_buffered_results_set::to_same_string" );
@ -1071,19 +1092,19 @@ SQLRETURN sqlsrv_buffered_result_set::to_same_string( SQLSMALLINT field_index, _
SQLSRV_ASSERT( to_copy >= 0, "Negative field length calculated in buffered result set" );
if( to_copy > 0 ) {
memcpy( buffer, field_data + read_so_far, to_copy );
memcpy_s( buffer, buffer_length, field_data + read_so_far, to_copy );
read_so_far += to_copy;
}
if( extra ) {
OACR_WARNING_SUPPRESS( 26001, "Buffer length verified above" );
memcpy( reinterpret_cast<SQLCHAR*>( buffer ) + to_copy, L"\0", extra );
memcpy_s( reinterpret_cast<SQLCHAR*>( buffer ) + to_copy, buffer_length, L"\0", extra );
}
return r;
}
SQLRETURN sqlsrv_buffered_result_set::wide_to_system_string( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::wide_to_system_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( last_error == NULL, "Pending error for sqlsrv_buffered_results_set::wide_to_system_string" );
@ -1091,7 +1112,7 @@ SQLRETURN sqlsrv_buffered_result_set::wide_to_system_string( SQLSMALLINT field_i
unsigned char* row = get_row();
SQLCHAR* field_data = NULL;
SQLULEN field_len = NULL;
SQLLEN field_len = NULL;
// if this is the first time called for this field, just convert the entire string to system first then
// use that to read from instead of converting chunk by chunk. This is because it's impossible to know
@ -1116,9 +1137,9 @@ SQLRETURN sqlsrv_buffered_result_set::wide_to_system_string( SQLSMALLINT field_i
char default_char = '?';
// allocate enough to handle WC -> DBCS conversion if it happens
temp_string = reinterpret_cast<SQLCHAR*>( sqlsrv_malloc( field_len, sizeof( char ), sizeof(char)));
temp_length = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR) field_data, field_len / sizeof(WCHAR),
(LPSTR) temp_string.get(), field_len, &default_char, &default_char_used );
temp_string = reinterpret_cast<SQLCHAR*>( sqlsrv_malloc( field_len, sizeof(char), sizeof(char)));
temp_length = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR) field_data, static_cast<int>(field_len / sizeof(WCHAR)),
(LPSTR) temp_string.get(), static_cast<int>(field_len), &default_char, &default_char_used );
if( temp_length == 0 ) {
@ -1156,7 +1177,7 @@ SQLRETURN sqlsrv_buffered_result_set::wide_to_system_string( SQLSMALLINT field_i
if( to_copy > 0 ) {
memcpy( buffer, temp_string.get() + read_so_far, to_copy );
memcpy_s( buffer, buffer_length, temp_string.get() + read_so_far, to_copy );
}
SQLSRV_ASSERT( to_copy >= 0, "Invalid field copy length" );
OACR_WARNING_SUPPRESS( BUFFER_UNDERFLOW, "Buffer length verified above" );
@ -1167,14 +1188,14 @@ SQLRETURN sqlsrv_buffered_result_set::wide_to_system_string( SQLSMALLINT field_i
}
SQLRETURN sqlsrv_buffered_result_set::to_binary_string( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::to_binary_string( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
return to_same_string( field_index, buffer, buffer_length, out_buffer_length );
}
SQLRETURN sqlsrv_buffered_result_set::to_long( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::to_long( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_LONG, "Invlid conversion to long" );
SQLSRV_ASSERT( buffer_length >= sizeof( LONG ), "Buffer too small for SQL_C_LONG" ); // technically should ignore this
@ -1182,14 +1203,14 @@ SQLRETURN sqlsrv_buffered_result_set::to_long( SQLSMALLINT field_index, __out vo
unsigned char* row = get_row();
LONG* long_data = reinterpret_cast<LONG*>( &row[ meta[ field_index ].offset ] );
memcpy( buffer, long_data, sizeof( LONG ));
memcpy_s( buffer, buffer_length, long_data, sizeof( LONG ));
*out_buffer_length = sizeof( LONG );
return SQL_SUCCESS;
}
SQLRETURN sqlsrv_buffered_result_set::to_double( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length )
SQLRETURN sqlsrv_buffered_result_set::to_double( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length )
{
SQLSRV_ASSERT( meta[ field_index ].c_type == SQL_C_DOUBLE, "Invlid conversion to double" );
SQLSRV_ASSERT( buffer_length >= sizeof( double ), "Buffer too small for SQL_C_DOUBLE" ); // technically should ignore this
@ -1197,7 +1218,7 @@ SQLRETURN sqlsrv_buffered_result_set::to_double( SQLSMALLINT field_index, __out
unsigned char* row = get_row();
double* double_data = reinterpret_cast<double*>( &row[ meta[ field_index ].offset ] );
memcpy( buffer, double_data, sizeof( double ));
memcpy_s( buffer, buffer_length, double_data, sizeof( double ));
*out_buffer_length = sizeof( double );
return SQL_SUCCESS;
@ -1206,9 +1227,9 @@ SQLRETURN sqlsrv_buffered_result_set::to_double( SQLSMALLINT field_index, __out
namespace {
// called for each row in the cache when the cache is destroyed in the destructor
void cache_row_dtor( void* data )
void cache_row_dtor( zval* data )
{
row_dtor_closure* cl = reinterpret_cast<row_dtor_closure*>( data );
row_dtor_closure* cl = reinterpret_cast<row_dtor_closure*>( Z_PTR_P( data ) );
BYTE* row = cl->row_data;
// don't release this here, since this is called from the destructor of the result_set
sqlsrv_buffered_result_set* result_set = cl->results;
@ -1223,13 +1244,14 @@ void cache_row_dtor( void* data )
}
sqlsrv_free( row );
sqlsrv_free( cl );
}
SQLPOINTER read_lob_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_buffered_result_set::meta_data& meta,
unsigned long mem_used TSRMLS_DC )
zend_long mem_used TSRMLS_DC )
{
SQLSMALLINT extra = 0;
SQLLEN* output_buffer_len = NULL;
SQLULEN* output_buffer_len = NULL;
// Set the amount of space necessary for null characters at the end of the data.
switch( meta.c_type ) {
@ -1259,7 +1281,7 @@ SQLPOINTER read_lob_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_b
do {
output_buffer_len = reinterpret_cast<SQLLEN*>( buffer.get() );
output_buffer_len = reinterpret_cast<SQLULEN*>( buffer.get() );
r = core::SQLGetData( stmt, field_index + 1, meta.c_type, buffer.get() + already_read + sizeof( SQLULEN ),
to_read - already_read + extra, &last_field_len, false /*handle_warning*/ TSRMLS_CC );
@ -1303,7 +1325,7 @@ SQLPOINTER read_lob_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_b
already_read += to_read - already_read;
to_read = last_field_len;
buffer.resize( to_read + extra + sizeof( SQLULEN ));
output_buffer_len = reinterpret_cast<SQLLEN*>( buffer.get() );
output_buffer_len = reinterpret_cast<SQLULEN*>( buffer.get() );
// record the size of the field since we have it available
*output_buffer_len = last_field_len;
full_length_returned = true;
@ -1318,7 +1340,7 @@ SQLPOINTER read_lob_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_b
throw core::CoreException();
}
buffer.resize( to_read + extra + sizeof( SQLULEN ));
output_buffer_len = reinterpret_cast<SQLLEN*>( buffer.get() );
output_buffer_len = reinterpret_cast<SQLULEN*>( buffer.get() );
}
} while( true );

View file

@ -6,7 +6,7 @@
//
// Contents: Core routines and constants shared by the Microsoft Drivers for PHP for SQL Server
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -107,7 +107,7 @@ OACR_WARNING_POP
#include <limits>
#include <cassert>
#include <strsafe.h>
#include <memory>
// included for SQL Server specific constants
#include "msodbcsql.h"
@ -182,7 +182,7 @@ union sqlsrv_sqltype {
int scale:8;
} typeinfo;
long value;
zend_long value;
};
@ -196,7 +196,7 @@ union sqlsrv_phptype {
unsigned encoding:16;
} typeinfo;
long value;
zend_long value;
};
// static assert for enforcing compile time conditions
@ -500,6 +500,16 @@ public:
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
@ -656,7 +666,7 @@ public:
void reset( zval* ptr = NULL )
{
if( _ptr )
zval_ptr_dtor( &_ptr );
zval_ptr_dtor(_ptr );
_ptr = ptr;
}
@ -664,12 +674,7 @@ public:
{
return sqlsrv_auto_ptr<zval, zval_auto_ptr>::operator=( ptr );
}
#if PHP_MAJOR_VERSION > 5 || (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 3)
operator zval_gc_info*( void )
{
return reinterpret_cast<zval_gc_info*>(_ptr);
}
#endif
private:
@ -884,6 +889,8 @@ class sqlsrv_context {
{
if( handle_ != SQL_NULL_HANDLE ) {
::SQLFreeHandle( handle_type_, handle_ );
last_error_.reset();
}
handle_ = SQL_NULL_HANDLE;
}
@ -919,7 +926,7 @@ const int SQLSRV_OS_VISTA_OR_LATER = 6; // major version for Vista
struct sqlsrv_encoding {
const char* iana;
unsigned int iana_len;
size_t iana_len;
unsigned int code_page;
bool not_for_connection;
@ -965,6 +972,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;
@ -976,6 +991,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 )
@ -1010,7 +1027,7 @@ const char ConnectionPooling[] = "ConnectionPooling";
const char Database[] = "Database";
const char Encrypt[] = "Encrypt";
const char Failover_Partner[] = "Failover_Partner";
const char LoginTimeout[] = "LoginTimggeout";
const char LoginTimeout[] = "LoginTimeout";
const char MARS_ODBC[] = "MARS_Connection";
const char MultiSubnetFailover[] = "MultiSubnetFailover";
const char QuotedId[] = "QuotedId";
@ -1087,9 +1104,8 @@ 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 )),
Z_STRLEN_P( value ) TSRMLS_CC );
static_cast<SQLINTEGER>(Z_STRLEN_P( value )) TSRMLS_CC );
}
catch( core::CoreException& ) {
throw;
@ -1118,15 +1134,15 @@ sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_
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, long sql_len 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 );
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 );
bool core_is_conn_opt_value_escaped( const char* value, int value_len );
int core_str_zval_is_true( zval* str_zval );
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 );
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
@ -1158,7 +1174,7 @@ struct stmt_option {
const char * name; // name of the statement option
unsigned int name_len; // name length
unsigned int key;
stmt_option_functor* func; // callback that actually handles the work of the option
std::unique_ptr<stmt_option_functor> func; // callback that actually handles the work of the option
};
@ -1170,7 +1186,7 @@ struct sqlsrv_stream {
SQLUSMALLINT field_index;
SQLSMALLINT sql_type;
sqlsrv_stmt* stmt;
int stmt_index;
std::size_t stmt_index;
sqlsrv_stream( zval* str_z, SQLSRV_ENCODING enc ) :
stream_z( str_z ), encoding( enc )
@ -1183,7 +1199,7 @@ struct sqlsrv_stream {
};
// close any active stream
void close_active_stream( __inout sqlsrv_stmt* stmt TSRMLS_DC );
void close_active_stream( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC );
extern php_stream_wrapper g_sqlsrv_stream_wrapper;
@ -1197,7 +1213,7 @@ struct sqlsrv_output_param {
zval* param_z;
SQLSRV_ENCODING encoding;
int param_num; // used to index into the ind_or_len of the statement
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;
@ -1239,28 +1255,28 @@ struct sqlsrv_stmt : public sqlsrv_context {
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
unsigned long buffered_query_limit; // maximum allowed memory for a buffered query (measured in KB)
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
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
zval* active_stream; // the currently active stream reading data from the database
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
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
virtual sqlsrv_phptype sql_type_to_php_type( SQLINTEGER sql_type, SQLUINTEGER size, bool prefer_string_to_stream ) = 0;
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;
};
@ -1302,25 +1318,25 @@ typedef sqlsrv_stmt* (*driver_stmt_factory)( sqlsrv_conn* conn, SQLHANDLE h, err
// *** 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, unsigned int param_num, int direction, zval* param_z,
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, SQLLEN 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 );
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);
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, unsigned int paramno, zval* param_z TSRMLS_DC );
void core_sqlsrv_set_scrollable( sqlsrv_stmt* stmt, unsigned int cursor_type TSRMLS_DC );
void core_sqlsrv_post_param( sqlsrv_stmt* stmt, zend_ulong paramno, zval* param_z TSRMLS_DC );
void core_sqlsrv_set_scrollable( sqlsrv_stmt* stmt, unsigned long cursor_type TSRMLS_DC );
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, long limit TSRMLS_DC );
void core_sqlsrv_set_buffered_query_limit( sqlsrv_stmt* stmt, SQLLEN limit TSRMLS_DC );
//*********************************************************************************************************************************
@ -1344,11 +1360,11 @@ struct sqlsrv_result_set {
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,
__out void* buffer, SQLLEN buffer_length, __out SQLLEN* out_buffer_length,
_Out_ void* buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length,
bool handle_warning TSRMLS_DC )= 0;
virtual SQLRETURN get_diag_field( SQLSMALLINT record_number, SQLSMALLINT diag_identifier,
__out SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
__out SQLSMALLINT* out_buffer_length TSRMLS_DC ) = 0;
_Out_ SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
_Out_ SQLSMALLINT* out_buffer_length TSRMLS_DC ) = 0;
virtual sqlsrv_error* get_diag_rec( SQLSMALLINT record_number ) = 0;
virtual SQLLEN row_count( TSRMLS_D ) = 0;
};
@ -1356,16 +1372,16 @@ struct sqlsrv_result_set {
struct sqlsrv_odbc_result_set : public sqlsrv_result_set {
explicit sqlsrv_odbc_result_set( sqlsrv_stmt* );
virtual ~sqlsrv_odbc_result_set( void );
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,
__out void* buffer, SQLLEN buffer_length, __out SQLLEN* out_buffer_length,
_Out_ void* buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length,
bool handle_warning TSRMLS_DC );
virtual SQLRETURN get_diag_field( SQLSMALLINT record_number, SQLSMALLINT diag_identifier,
__out SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
__out SQLSMALLINT* out_buffer_length TSRMLS_DC );
_Out_ SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
_Out_ SQLSMALLINT* out_buffer_length TSRMLS_DC );
virtual sqlsrv_error* get_diag_rec( SQLSMALLINT record_number );
virtual SQLLEN row_count( TSRMLS_D );
@ -1390,8 +1406,8 @@ struct sqlsrv_buffered_result_set : public sqlsrv_result_set {
// 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 unsigned long BUFFERED_QUERY_LIMIT_DEFAULT = 10240; // measured in KB
static const long BUFFERED_QUERY_LIMIT_INVALID = 0;
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 );
@ -1399,11 +1415,11 @@ struct sqlsrv_buffered_result_set : public sqlsrv_result_set {
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,
__out void* buffer, SQLLEN buffer_length, __out SQLLEN* out_buffer_length,
_Out_ void* buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length,
bool handle_warning TSRMLS_DC );
virtual SQLRETURN get_diag_field( SQLSMALLINT record_number, SQLSMALLINT diag_identifier,
__out SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
__out SQLSMALLINT* out_buffer_length TSRMLS_DC );
_Out_ SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
_Out_ SQLSMALLINT* out_buffer_length TSRMLS_DC );
virtual sqlsrv_error* get_diag_rec( SQLSMALLINT record_number );
virtual SQLLEN row_count( TSRMLS_D );
@ -1434,55 +1450,55 @@ struct sqlsrv_buffered_result_set : public sqlsrv_result_set {
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
typedef SQLRETURN (sqlsrv_buffered_result_set::*conv_fn)( SQLSMALLINT field_index, __out void* buffer, SQLLEN buffer_length,
__out SQLLEN* out_buffer_length );
typedef SQLRETURN (sqlsrv_buffered_result_set::*conv_fn)( SQLSMALLINT field_index, _Out_ void* buffer, SQLLEN buffer_length,
_Out_ SQLLEN* out_buffer_length );
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
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 );
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 );
// long conversion functions
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 );
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 );
// double conversion functions
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 );
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 );
// string to number conversion functions
// Future: See if these can be converted directly to template member functions
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 );
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 );
// utility functions for conversions
unsigned char* get_row( void );
@ -1500,11 +1516,12 @@ struct sqlsrv_buffered_result_set : public sqlsrv_result_set {
#define MEMCHECK_SILENT 1
// utility functions shared by multiple callers across files
bool convert_string_from_utf16_inplace( SQLSRV_ENCODING encoding, char** string, SQLINTEGER& len);
bool convert_string_from_utf16( SQLSRV_ENCODING encoding, const wchar_t* inString, SQLINTEGER cchInLen, char** outString, SQLINTEGER& cchOutLen );
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,
unsigned int mbcs_len, __out unsigned int* utf16_len );
unsigned int mbcs_len, _Out_ unsigned int* utf16_len );
//*********************************************************************************************************************************
// Error handling routines and Predefined Errors
@ -1559,8 +1576,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[];
@ -1582,7 +1599,7 @@ enum error_handling_flags {
// 2/code) driver specific error code
// 3/message) driver specific error message
// The fetch type determines if the indices are numeric, associative, or both.
bool core_sqlsrv_get_odbc_error( sqlsrv_context& ctx, int record_number, __out sqlsrv_error_auto_ptr& error,
bool core_sqlsrv_get_odbc_error( sqlsrv_context& ctx, int record_number, _Out_ sqlsrv_error_auto_ptr& error,
logging_severity severity TSRMLS_DC );
// format and return a driver specfic error
@ -1595,11 +1612,11 @@ void core_sqlsrv_format_driver_error( sqlsrv_context& ctx, sqlsrv_error_const co
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
DWORD core_sqlsrv_format_message( char* output_buffer, unsigned output_len, const char* format, ... );
DWORD core_sqlsrv_format_message( char*& output_buffer, unsigned output_len, const char* format, ... );
// 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 int sqlsrv_error_code TSRMLS_DC, bool warning, ... )
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 );
@ -1608,7 +1625,7 @@ inline bool call_error_handler( sqlsrv_context& ctx, unsigned int sqlsrv_error_c
return ignored;
}
inline bool call_error_handler( sqlsrv_context* ctx, unsigned int sqlsrv_error_code TSRMLS_DC, bool warning, ... )
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 );
@ -1737,9 +1754,8 @@ 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( !strcmp( reinterpret_cast<const char*>( err_msg ), CONNECTION_BUSY_ODBC_ERROR[driver_version] )) {
THROW_CORE_ERROR( stmt, SQLSRV_ERROR_MARS_OFF );
}
@ -1755,8 +1771,8 @@ namespace core {
// the context to hold the error, they are not passed as const.
inline SQLRETURN SQLGetDiagField( sqlsrv_context* ctx, SQLSMALLINT record_number, SQLSMALLINT diag_identifier,
__out SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
__out SQLSMALLINT* out_buffer_length TSRMLS_DC )
_Out_ SQLPOINTER diag_info_buffer, SQLSMALLINT buffer_length,
_Out_ SQLSMALLINT* out_buffer_length TSRMLS_DC )
{
SQLRETURN r = ::SQLGetDiagField( ctx->handle_type(), ctx->handle(), record_number, diag_identifier,
diag_info_buffer, buffer_length, out_buffer_length );
@ -1769,7 +1785,7 @@ namespace core {
}
inline void SQLAllocHandle( SQLSMALLINT HandleType, sqlsrv_context& InputHandle,
__out_ecount(1) SQLHANDLE* OutputHandlePtr TSRMLS_DC )
_Out_writes_(1) SQLHANDLE* OutputHandlePtr TSRMLS_DC )
{
SQLRETURN r;
r = ::SQLAllocHandle( HandleType, InputHandle.handle(), OutputHandlePtr );
@ -1785,9 +1801,9 @@ namespace core {
SQLSMALLINT ParameterType,
SQLULEN ColumnSize,
SQLSMALLINT DecimalDigits,
__inout SQLPOINTER ParameterValuePtr,
_Inout_ SQLPOINTER ParameterValuePtr,
SQLLEN BufferLength,
__inout SQLLEN * StrLen_Or_IndPtr
_Inout_ SQLLEN * StrLen_Or_IndPtr
TSRMLS_DC )
{
SQLRETURN r;
@ -1801,8 +1817,8 @@ namespace core {
inline void SQLColAttribute( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, SQLUSMALLINT field_identifier,
__out SQLPOINTER field_type_char, SQLSMALLINT buffer_length,
__out SQLSMALLINT* out_buffer_length, __out SQLLEN* field_type_num TSRMLS_DC )
_Out_ SQLPOINTER field_type_char, SQLSMALLINT buffer_length,
_Out_ SQLSMALLINT* out_buffer_length, _Out_ SQLLEN* field_type_num TSRMLS_DC )
{
SQLRETURN r = ::SQLColAttribute( stmt->handle(), field_index, field_identifier, field_type_char,
buffer_length, out_buffer_length, field_type_num );
@ -1813,9 +1829,9 @@ namespace core {
}
inline void SQLDescribeCol( sqlsrv_stmt* stmt, SQLSMALLINT colno, __out_z 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 )
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 )
{
SQLRETURN r;
r = ::SQLDescribeCol( stmt->handle(), colno, col_name, col_name_length, col_name_length_out,
@ -1898,7 +1914,7 @@ namespace core {
}
inline SQLRETURN SQLGetData( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, SQLSMALLINT target_type,
__out void* buffer, SQLLEN buffer_length, __out SQLLEN* out_buffer_length,
_Out_ void* buffer, SQLLEN buffer_length, _Out_ SQLLEN* out_buffer_length,
bool handle_warning TSRMLS_DC )
{
SQLRETURN r = ::SQLGetData( stmt->handle(), field_index, target_type, buffer, buffer_length, out_buffer_length );
@ -1920,8 +1936,8 @@ namespace core {
}
inline void SQLGetInfo( sqlsrv_conn* conn, SQLUSMALLINT info_type, __out SQLPOINTER info_value, SQLSMALLINT buffer_len,
__out SQLSMALLINT* str_len TSRMLS_DC )
inline void SQLGetInfo( sqlsrv_conn* conn, SQLUSMALLINT info_type, _Out_ SQLPOINTER info_value, SQLSMALLINT buffer_len,
_Out_ SQLSMALLINT* str_len TSRMLS_DC )
{
SQLRETURN r;
r = ::SQLGetInfo( conn->handle(), info_type, info_value, buffer_len, str_len );
@ -1970,7 +1986,7 @@ namespace core {
// 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.
inline SQLRETURN SQLParamData( sqlsrv_stmt* stmt, __out SQLPOINTER* value_ptr_ptr TSRMLS_DC )
inline SQLRETURN SQLParamData( sqlsrv_stmt* stmt, _Out_ SQLPOINTER* value_ptr_ptr TSRMLS_DC )
{
SQLRETURN r;
r = ::SQLParamData( stmt->handle(), value_ptr_ptr );
@ -2055,6 +2071,25 @@ namespace core {
// *** zend wrappers ***
//zend_resource_dtor sets the type of destroyed resources to -1
#define RSRC_INVALID_TYPE -1
// 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
@ -2063,7 +2098,7 @@ namespace core {
// 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, unsigned int index, zval* value TSRMLS_DC)
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 ) {
@ -2079,7 +2114,7 @@ namespace core {
}
}
inline void sqlsrv_add_assoc_null( sqlsrv_context& ctx, zval* array_z, char* key TSRMLS_DC )
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 ) {
@ -2087,7 +2122,7 @@ namespace core {
}
}
inline void sqlsrv_add_assoc_long( sqlsrv_context& ctx, zval* array_z, char* key, long val TSRMLS_DC )
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 ) {
@ -2095,23 +2130,26 @@ namespace core {
}
}
inline void sqlsrv_add_assoc_string( sqlsrv_context& ctx, zval* array_z, char* key, char* val, bool duplicate TSRMLS_DC )
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, duplicate );
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);
}
}
inline void sqlsrv_array_init( sqlsrv_context& ctx, __out zval* new_array TSRMLS_DC)
inline void sqlsrv_array_init( sqlsrv_context& ctx, _Out_ zval* new_array TSRMLS_DC)
{
int zr = ::array_init( new_array );
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 )
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 );
@ -2120,53 +2158,89 @@ namespace core {
}
}
inline void sqlsrv_zend_hash_get_current_data( sqlsrv_context& ctx, HashTable* ht, __out void** output_data TSRMLS_DC )
inline void sqlsrv_zend_hash_get_current_data(sqlsrv_context& ctx, HashTable* ht, _Out_ zval*& output_data TSRMLS_DC)
{
int zr = (output_data = ::zend_hash_get_current_data(ht)) != NULL ? SUCCESS : FAILURE;
CHECK_ZEND_ERROR( zr, ctx, SQLSRV_ERROR_ZEND_HASH ) {
throw CoreException();
}
}
inline void sqlsrv_zend_hash_get_current_data_ptr(sqlsrv_context& ctx, HashTable* ht, _Out_ void*& output_data TSRMLS_DC)
{
int zr = ::zend_hash_get_current_data( ht, output_data );
CHECK_ZEND_ERROR( zr, ctx, SQLSRV_ERROR_ZEND_HASH ) {
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, int index TSRMLS_DC )
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, unsigned long index, void* data,
uint data_size TSRMLS_DC )
inline void sqlsrv_zend_hash_index_update( sqlsrv_context& ctx, HashTable* ht, zend_ulong index, zval* data_z TSRMLS_DC )
{
int zr = ::zend_hash_index_update( ht, index, data, data_size, NULL );
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_next_index_insert( sqlsrv_context& ctx, HashTable* ht, void* data,
uint data_size TSRMLS_DC )
inline void sqlsrv_zend_hash_index_update_ptr(sqlsrv_context& ctx, HashTable* ht, zend_ulong index, void* pData TSRMLS_DC)
{
int zr = ::zend_hash_next_index_insert( ht, data, data_size, NULL );
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_init( sqlsrv_context& ctx, HashTable* ht, unsigned int initial_size, hash_func_t hash_fn,
dtor_func_t dtor_fn, zend_bool persistent TSRMLS_DC )
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 = ::zend_hash_init( ht, initial_size, hash_fn, dtor_fn, persistent );
CHECK_ZEND_ERROR( zr, ctx, SQLSRV_ERROR_ZEND_HASH ) {
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_add( sqlsrv_context& ctx, HashTable* ht, char* key, unsigned int key_len, void** data,
unsigned int data_size, void **pDest TSRMLS_DC )
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 )
{
int zr = ::zend_hash_add( ht, key, key_len, data, data_size, pDest );
::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();
}

View file

@ -3,7 +3,7 @@
//
// Contents: Core routines that use statement handles shared between sqlsrv and pdo_sqlsrv
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -24,7 +24,7 @@ namespace {
// certain drivers using this layer will call for repeated or out of order field retrievals. To allow this, we cache the
// results of every field request, and if it is out of order, we cache those for preceding fields.
struct field_cache {
void* value;
SQLLEN len;
sqlsrv_phptype type;
@ -35,7 +35,7 @@ struct field_cache {
// if the value is NULL, then just record a NULL pointer
if( field_value != NULL ) {
value = sqlsrv_malloc( field_len );
memcpy( value, field_value, field_len );
memcpy_s( value, field_len, field_value, field_len );
len = field_len;
}
else {
@ -76,36 +76,36 @@ const size_t DATE_FORMAT_LEN = sizeof( DATE_FORMAT );
// *** internal functions ***
// Only declarations are put here. Functions contain the documentation they need at their definition sites.
void calc_string_size( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, SQLLEN sql_type, __out SQLLEN& size TSRMLS_DC );
void calc_string_size( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, SQLLEN sql_type, _Out_ SQLLEN& size TSRMLS_DC );
size_t calc_utf8_missing( sqlsrv_stmt* stmt, const char* buffer, size_t buffer_end TSRMLS_DC );
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 );
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);
// returns the ODBC C type constant that matches the PHP type and encoding given
SQLSMALLINT default_c_type( sqlsrv_stmt* stmt, unsigned int paramno, zval const* param_z, SQLSRV_ENCODING encoding TSRMLS_DC );
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,
__out SQLULEN& column_size, __out SQLSMALLINT& decimal_digits TSRMLS_DC );
_Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits TSRMLS_DC );
// given a zval and encoding, determine the appropriate sql type, column size, and decimal scale (if appropriate)
void default_sql_type( sqlsrv_stmt* stmt, unsigned int paramno, zval* param_z, SQLSRV_ENCODING encoding,
__out SQLSMALLINT& sql_type TSRMLS_DC );
void field_cache_dtor( void* data );
void default_sql_type( sqlsrv_stmt* stmt, SQLULEN paramno, zval* param_z, SQLSRV_ENCODING encoding,
_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 );
stmt_option const* get_stmt_option( sqlsrv_conn const* conn, unsigned long key, const stmt_option stmt_opts[] 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
void resize_output_buffer_if_necessary( sqlsrv_stmt* stmt, zval* param_z, unsigned int paramno, SQLSRV_ENCODING encoding,
void resize_output_buffer_if_necessary( sqlsrv_stmt* stmt, zval* param_z, SQLULEN paramno, SQLSRV_ENCODING encoding,
SQLSMALLINT c_type, SQLSMALLINT sql_type, SQLULEN column_size, SQLPOINTER& buffer,
SQLLEN& buffer_len TSRMLS_DC );
void save_output_param_for_later( sqlsrv_stmt* stmt, sqlsrv_output_param& param TSRMLS_DC );
// send all the stream data
void send_param_streams( sqlsrv_stmt* stmt TSRMLS_DC );
// called when a bound output string parameter is to be destroyed
void sqlsrv_output_param_dtor( void* data );
void sqlsrv_output_param_dtor( zval* data );
// called when a bound stream parameter is to be destroyed.
void sqlsrv_stream_dtor( void* data );
void sqlsrv_stream_dtor( zval* data );
bool is_streamable_type( SQLINTEGER sql_type );
}
@ -127,43 +127,32 @@ 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)
MAKE_STD_ZVAL( param_input_strings );
core::sqlsrv_array_init( *conn, param_input_strings TSRMLS_CC );
core::sqlsrv_array_init( *conn, &param_input_strings TSRMLS_CC );
// initialize the (input only) stream parameters (which holds sqlsrv_stream structures)
MAKE_STD_ZVAL( param_streams );
Z_TYPE_P( param_streams ) = IS_ARRAY;
ALLOC_HASHTABLE( Z_ARRVAL_P( param_streams ));
core::sqlsrv_zend_hash_init( *conn, Z_ARRVAL_P( param_streams ), 5 /* # of buckets */, NULL /*hashfn*/,
sqlsrv_stream_dtor, 0 /*persistent*/ TSRMLS_CC );
ZVAL_NEW_ARR( &param_streams );
core::sqlsrv_zend_hash_init(*conn, Z_ARRVAL( param_streams ), 5 /* # of buckets */, sqlsrv_stream_dtor, 0 /*persistent*/ TSRMLS_CC);
// initialize the (input only) datetime parameters of converted date time objects to strings
MAKE_STD_ZVAL( param_datetime_buffers );
array_init( param_datetime_buffers );
array_init( &param_datetime_buffers );
// initialize the output string parameters (which holds sqlsrv_output_param structures)
MAKE_STD_ZVAL( output_params );
Z_TYPE_P( output_params ) = IS_ARRAY;
ALLOC_HASHTABLE( Z_ARRVAL_P( output_params ));
core::sqlsrv_zend_hash_init( *conn, Z_ARRVAL_P( output_params ), 5 /* # of buckets */, NULL /*hashfn*/,
sqlsrv_output_param_dtor, 0 /*persistent*/ TSRMLS_CC );
ZVAL_NEW_ARR( &output_params );
core::sqlsrv_zend_hash_init(*conn, Z_ARRVAL( output_params ), 5 /* # of buckets */, sqlsrv_output_param_dtor, 0 /*persistent*/ TSRMLS_CC);
// initialize the field cache
MAKE_STD_ZVAL( field_cache );
Z_TYPE_P( field_cache ) = IS_ARRAY;
ALLOC_HASHTABLE( Z_ARRVAL_P( field_cache ));
core::sqlsrv_zend_hash_init( *conn, Z_ARRVAL_P( field_cache ), 5 /* # of buckets */, NULL /*hashfn*/,
field_cache_dtor, 0 /*persistent*/ TSRMLS_CC );
ZVAL_NEW_ARR( &field_cache );
core::sqlsrv_zend_hash_init(*conn, Z_ARRVAL(field_cache), 5 /* # of buckets */, field_cache_dtor, 0 /*persistent*/ TSRMLS_CC);
}
// 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 );
}
@ -174,8 +163,8 @@ sqlsrv_stmt::~sqlsrv_stmt( void )
efree( current_results );
current_results = NULL;
}
invalidate();
invalidate();
zval_ptr_dtor( &param_input_strings );
zval_ptr_dtor( &output_params );
zval_ptr_dtor( &param_streams );
@ -189,13 +178,13 @@ sqlsrv_stmt::~sqlsrv_stmt( void )
// execution phase.
void sqlsrv_stmt::free_param_data( TSRMLS_D )
{
SQLSRV_ASSERT( Z_TYPE_P( param_input_strings ) == IS_ARRAY && Z_TYPE_P( param_streams ) == IS_ARRAY,
SQLSRV_ASSERT(Z_TYPE( param_input_strings ) == IS_ARRAY && Z_TYPE( param_streams ) == IS_ARRAY,
"sqlsrv_stmt::free_param_data: Param zvals aren't arrays." );
zend_hash_clean( Z_ARRVAL_P( param_input_strings ));
zend_hash_clean( Z_ARRVAL_P( output_params ));
zend_hash_clean( Z_ARRVAL_P( param_streams ));
zend_hash_clean( Z_ARRVAL_P( param_datetime_buffers ));
zend_hash_clean( Z_ARRVAL_P( field_cache ));
zend_hash_clean( Z_ARRVAL( param_input_strings ));
zend_hash_clean( Z_ARRVAL( output_params ));
zend_hash_clean( Z_ARRVAL( param_streams ));
zend_hash_clean( Z_ARRVAL( param_datetime_buffers ));
zend_hash_clean( Z_ARRVAL( field_cache ));
}
@ -242,7 +231,7 @@ void sqlsrv_stmt::new_result_set( TSRMLS_D )
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 )
{
sqlsrv_malloc_auto_ptr<sqlsrv_stmt> stmt;
sqlsrv_malloc_auto_ptr<sqlsrv_stmt> stmt;
SQLHANDLE stmt_h = SQL_NULL_HANDLE;
try {
@ -259,34 +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 ) {
zend_ulong index = -1;
zend_string *key = NULL;
zval* value_z = NULL;
for( zend_hash_internal_pointer_reset( options_ht );
zend_hash_has_more_elements( options_ht ) == SUCCESS;
zend_hash_move_forward( options_ht )) {
ZEND_HASH_FOREACH_KEY_VAL( options_ht, index, key, value_z ) {
char *key = NULL;
unsigned int key_len = 0;
unsigned long index = -1;
zval** value_z = NULL;
int type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG;
int type = zend_hash_get_current_key_ex( options_ht, &key, &key_len, &index, 0, NULL );
// 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, (void**) &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 );
}
// The driver layer should ensure a valid key.
DEBUG_SQLSRV_ASSERT(( type == HASH_KEY_IS_LONG ), "allocate_stmt: Invalid statment option key provided." );
zend_hash_internal_pointer_end( options_ht );
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;
@ -329,9 +310,9 @@ sqlsrv_stmt* core_sqlsrv_create_stmt( sqlsrv_conn* conn, driver_stmt_factory stm
// Return:
// Nothing, though an exception is thrown if an error occurs
// The php type of the parameter is taken from the zval.
// The sql type is given as a hint if the driver provides it.
// The sql type is given as a hint if the driver provides it.
void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int direction, zval* param_z,
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 )
{
@ -352,13 +333,17 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int dire
}
// resize the statements array of int_ptrs if the parameter isn't already set.
if( stmt->param_ind_ptrs.size() < param_num + 1 ) {
if( stmt->param_ind_ptrs.size() < static_cast<size_t>(param_num + 1) ) {
stmt->param_ind_ptrs.resize( param_num + 1, SQL_NULL_DATA );
}
SQLLEN& ind_ptr = stmt->param_ind_ptrs[ param_num ];
bool zval_was_null = (Z_TYPE_P( param_z ) == IS_NULL);
bool zval_was_bool = (Z_TYPE_P( param_z ) == IS_BOOL);
zval* param_ref = param_z;
if ( Z_ISREF_P( param_z ) ) {
ZVAL_DEREF( param_z );
}
bool zval_was_null = ( Z_TYPE_P( param_z ) == IS_NULL );
bool zval_was_bool = ( Z_TYPE_P( param_z ) == IS_TRUE || Z_TYPE_P( param_z ) == IS_FALSE );
// if the user asks for for a specific type for input and output, make sure the data type we send matches the data we
// type we expect back, since we can only send and receive the same type. Anything can be converted to a string, so
// we always let that match if they want a string back.
@ -426,12 +411,12 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int dire
// if the sql type is unknown, then set the default based on the PHP type passed in
if( sql_type == SQL_UNKNOWN_TYPE ) {
default_sql_type( stmt, param_num, param_z, encoding, sql_type TSRMLS_CC );
default_sql_type( stmt, param_num, param_z, encoding, sql_type TSRMLS_CC );
}
// if the size is unknown, then set the default based on the PHP type passed in
if( column_size == SQLSRV_UNKNOWN_SIZE ) {
default_sql_size_and_scale( stmt, param_num, param_z, encoding, column_size, decimal_digits TSRMLS_CC );
default_sql_size_and_scale( stmt, static_cast<unsigned int>( param_num ), param_z, encoding, column_size, decimal_digits TSRMLS_CC );
}
// determine the ODBC C type
@ -448,15 +433,18 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int dire
buffer_len = 0;
}
break;
case IS_BOOL:
case IS_TRUE:
case IS_FALSE:
case IS_LONG:
{
{
// if it is boolean, set the lval to 0 or 1
convert_to_long( param_z );
buffer = &param_z->value;
buffer_len = sizeof( param_z->value.lval );
buffer_len = sizeof( Z_LVAL_P( param_z ));
ind_ptr = buffer_len;
if( direction != SQL_PARAM_INPUT ) {
// save the parameter so that 1) the buffer doesn't go away, and 2) we can set it to NULL if returned
sqlsrv_output_param output_param( param_z, param_num, zval_was_bool );
sqlsrv_output_param output_param( param_ref, static_cast<int>( param_num ), zval_was_bool );
save_output_param_for_later( stmt, output_param TSRMLS_CC );
}
}
@ -464,11 +452,11 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int dire
case IS_DOUBLE:
{
buffer = &param_z->value;
buffer_len = sizeof( param_z->value.dval );
buffer_len = sizeof( Z_DVAL_P( param_z ));
ind_ptr = buffer_len;
if( direction != SQL_PARAM_INPUT ) {
// save the parameter so that 1) the buffer doesn't go away, and 2) we can set it to NULL if returned
sqlsrv_output_param output_param( param_z, param_num, false );
sqlsrv_output_param output_param( param_ref, static_cast<int>( param_num ), false );
save_output_param_for_later( stmt, output_param TSRMLS_CC );
}
}
@ -479,48 +467,43 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int dire
// if the encoding is UTF-8, translate from UTF-8 to UTF-16 (the type variables should have already been adjusted)
if( direction == SQL_PARAM_INPUT && encoding == CP_UTF8 ) {
zval_auto_ptr wbuffer_z;
ALLOC_INIT_ZVAL( wbuffer_z );
zval wbuffer_z;
ZVAL_NULL( &wbuffer_z );
bool converted = convert_input_param_to_utf16( param_z, wbuffer_z );
bool converted = convert_input_param_to_utf16( param_z, &wbuffer_z );
CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_INPUT_PARAM_ENCODING_TRANSLATE,
param_num + 1, get_last_error_message() ) {
throw core::CoreException();
}
buffer = Z_STRVAL_P( wbuffer_z );
buffer_len = Z_STRLEN_P( wbuffer_z );
core::sqlsrv_add_index_zval( *stmt, stmt->param_input_strings, param_num, wbuffer_z TSRMLS_CC );
wbuffer_z.transferred();
buffer = Z_STRVAL_P( &wbuffer_z );
buffer_len = Z_STRLEN_P( &wbuffer_z );
core::sqlsrv_add_index_zval(*stmt, &(stmt->param_input_strings), param_num, &wbuffer_z TSRMLS_CC);
}
ind_ptr = buffer_len;
if( direction != SQL_PARAM_INPUT ) {
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 4
// PHP 5.4 added interned strings, so since we obviously want to change that string here in some fashion,
// we reallocate the string if it's interned
if( IS_INTERNED( buffer )) {
ZVAL_STRINGL( param_z, static_cast<const char*>(buffer), buffer_len, 1 );
buffer = Z_STRVAL_P( param_z );
buffer_len = Z_STRLEN_P( param_z );
}
#endif
// PHP 5.4 added interned strings, so since we obviously want to change that string here in some fashion,
// we reallocate the string if it's interned
if ( ZSTR_IS_INTERNED( Z_STR_P( param_z ))) {
core::sqlsrv_zval_stringl( param_z, static_cast<const char*>(buffer), buffer_len );
buffer = Z_STRVAL_P( param_z );
buffer_len = Z_STRLEN_P( param_z );
}
// if it's a UTF-8 input output parameter (signified by the C type being SQL_C_WCHAR)
// or if the PHP type is a binary encoded string with a N(VAR)CHAR/NTEXTSQL type,
// convert it to wchar first
if( direction == SQL_PARAM_INPUT_OUTPUT &&
(c_type == SQL_C_WCHAR ||
(c_type == SQL_C_BINARY &&
(sql_type == SQL_WCHAR ||
sql_type == SQL_WVARCHAR ||
sql_type == SQL_WLONGVARCHAR )))) {
( c_type == SQL_C_WCHAR ||
( c_type == SQL_C_BINARY &&
( sql_type == SQL_WCHAR ||
sql_type == SQL_WVARCHAR ||
sql_type == SQL_WLONGVARCHAR )))) {
bool converted = convert_input_param_to_utf16( param_z, param_z );
CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_INPUT_PARAM_ENCODING_TRANSLATE,
param_num + 1, get_last_error_message() ) {
throw core::CoreException();
}
sqlsrv_free( buffer );
buffer = Z_STRVAL_P( param_z );
buffer_len = Z_STRLEN_P( param_z );
ind_ptr = buffer_len;
@ -532,11 +515,12 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int dire
buffer, buffer_len TSRMLS_CC );
// save the parameter to be adjusted and/or converted after the results are processed
sqlsrv_output_param output_param( param_z, encoding, param_num, buffer_len );
sqlsrv_output_param output_param( param_ref, encoding, param_num, static_cast<SQLUINTEGER>( buffer_len ));
save_output_param_for_later( stmt, output_param TSRMLS_CC );
// For output parameters, if we set the column_size to be same as the buffer_len,
// than if there is a truncation due to the data coming from the server being
// then if there is a truncation due to the data coming from the server being
// greater than the column_size, we don't get any truncation error. In order to
// avoid this silent truncation, we set the column_size to be "MAX" size for
// string types. This will guarantee that there is no silent truncation for
@ -561,11 +545,10 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int dire
{
SQLSRV_ASSERT( direction == SQL_PARAM_INPUT, "Invalid output param type. The driver layer should catch this." );
sqlsrv_stream stream_encoding( param_z, encoding );
HashTable* streams_ht = Z_ARRVAL_P( stmt->param_streams );
core::sqlsrv_zend_hash_index_update( *stmt, streams_ht, param_num, &stream_encoding, sizeof( stream_encoding )
TSRMLS_CC );
HashTable* streams_ht = Z_ARRVAL( stmt->param_streams );
core::sqlsrv_zend_hash_index_update_mem( *stmt, streams_ht, param_num, &stream_encoding, sizeof(stream_encoding) TSRMLS_CC );
buffer = reinterpret_cast<SQLPOINTER>( param_num );
zval_add_ref( &param_z ); // so that it doesn't go away while we're using it
Z_TRY_ADDREF_P( param_z ); // so that it doesn't go away while we're using it
buffer_len = 0;
ind_ptr = SQL_DATA_AT_EXEC;
}
@ -573,18 +556,23 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int dire
case IS_OBJECT:
{
SQLSRV_ASSERT( direction == SQL_PARAM_INPUT, "Invalid output param type. The driver layer should catch this." );
zval_auto_ptr function_z;
zval_auto_ptr buffer_z;
zval_auto_ptr format_z;
zval* params[1];
bool valid_class_name_found = false;
zval function_z;
zval buffer_z;
zval format_z;
zval params[1];
ZVAL_UNDEF( &function_z );
ZVAL_UNDEF( &buffer_z );
ZVAL_UNDEF( &format_z );
ZVAL_UNDEF( params );
zend_class_entry *class_entry = zend_get_class_entry( param_z TSRMLS_CC );
bool valid_class_name_found = false;
zend_class_entry *class_entry = Z_OBJCE_P( param_z TSRMLS_CC );
while( class_entry != NULL ) {
if( class_entry->name_length == DateTime::DATETIME_CLASS_NAME_LEN && class_entry->name != NULL &&
stricmp( class_entry->name, DateTime::DATETIME_CLASS_NAME ) == 0 ) {
if( class_entry->name->len == DateTime::DATETIME_CLASS_NAME_LEN && class_entry->name != NULL &&
stricmp( class_entry->name->val, DateTime::DATETIME_CLASS_NAME ) == 0 ) {
valid_class_name_found = true;
break;
}
@ -599,40 +587,37 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int dire
CHECK_CUSTOM_ERROR( !valid_class_name_found, stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, param_num + 1 ) {
throw core::CoreException();
}
ALLOC_INIT_ZVAL( buffer_z );
ALLOC_INIT_ZVAL( function_z );
ALLOC_INIT_ZVAL( format_z );
// if the user specifies the 'date' sql type, giving it the normal format will cause a 'date overflow error'
// meaning there is too much information in the character string. If the user specifies the 'datetimeoffset'
// sql type, it lacks the timezone.
if( sql_type == SQL_SS_TIMESTAMPOFFSET ) {
ZVAL_STRINGL( format_z, const_cast<char*>( DateTime::DATETIMEOFFSET_FORMAT ),
DateTime::DATETIMEOFFSET_FORMAT_LEN, 1 /* dup */ );
core::sqlsrv_zval_stringl( &format_z, const_cast<char*>( DateTime::DATETIMEOFFSET_FORMAT ),
DateTime::DATETIMEOFFSET_FORMAT_LEN );
}
else if( sql_type == SQL_TYPE_DATE ) {
ZVAL_STRINGL( format_z, const_cast<char*>( DateTime::DATE_FORMAT ), DateTime::DATE_FORMAT_LEN, 1 /* dup */ );
core::sqlsrv_zval_stringl( &format_z, const_cast<char*>( DateTime::DATE_FORMAT ), DateTime::DATE_FORMAT_LEN );
}
else {
ZVAL_STRINGL( format_z, const_cast<char*>( DateTime::DATETIME_FORMAT ), DateTime::DATETIME_FORMAT_LEN,
1 /* dup */);
core::sqlsrv_zval_stringl( &format_z, const_cast<char*>( DateTime::DATETIME_FORMAT ), DateTime::DATETIME_FORMAT_LEN );
}
// call the DateTime::format member function to convert the object to a string that SQL Server understands
ZVAL_STRINGL( function_z, "format", sizeof( "format" ) - 1, 1 );
core::sqlsrv_zval_stringl( &function_z, "format", sizeof( "format" ) - 1 );
params[0] = format_z;
// This is equivalent to the PHP code: $param_z->format( $format_z ); where param_z is the
// DateTime object and $format_z is the format string.
int zr = call_user_function( EG( function_table ), &param_z, function_z, buffer_z, 1, params TSRMLS_CC );
int zr = call_user_function( EG( function_table ), param_z, &function_z, &buffer_z, 1, params TSRMLS_CC );
zend_string_release( Z_STR( format_z ));
zend_string_release( Z_STR( function_z ));
CHECK_CUSTOM_ERROR( zr == FAILURE, stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, param_num + 1 ) {
throw core::CoreException();
}
buffer = Z_STRVAL_P( buffer_z );
zr = add_next_index_zval( stmt->param_datetime_buffers, buffer_z );
buffer = Z_STRVAL( buffer_z );
zr = add_next_index_zval( &( stmt->param_datetime_buffers ), &buffer_z );
CHECK_CUSTOM_ERROR( zr == FAILURE, stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, param_num + 1 ) {
throw core::CoreException();
}
buffer_len = Z_STRLEN_P( buffer_z );
buffer_z.transferred();
buffer_len = Z_STRLEN( buffer_z ) - 1;
ind_ptr = buffer_len;
break;
}
@ -649,9 +634,8 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int dire
ind_ptr = SQL_NULL_DATA;
}
core::SQLBindParameter( stmt, param_num + 1, direction, c_type, sql_type, column_size, decimal_digits, buffer, buffer_len,
&ind_ptr TSRMLS_CC );
core::SQLBindParameter( stmt, param_num + 1, direction,
c_type, sql_type, column_size, decimal_digits, buffer, buffer_len, &ind_ptr TSRMLS_CC );
}
catch( core::CoreException& e ) {
stmt->free_param_data( TSRMLS_C );
@ -670,13 +654,13 @@ void core_sqlsrv_bind_param( sqlsrv_stmt* stmt, unsigned int param_num, int dire
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;
@ -712,17 +696,24 @@ void core_sqlsrv_execute( sqlsrv_stmt* stmt TSRMLS_DC, const char* sql, int sql_
stmt->executed = true;
// if all the data has been sent and no data was returned then finalize the output parameters
if( stmt->send_streams_at_exec && (r == SQL_NO_DATA || !core_sqlsrv_has_any_result( stmt TSRMLS_CC ))) {
if( stmt->send_streams_at_exec && ( r == SQL_NO_DATA || !core_sqlsrv_has_any_result( stmt TSRMLS_CC ))) {
finalize_output_parameters( stmt TSRMLS_CC );
}
// stream parameters are sent, clean the Hashtable
if ( stmt->send_streams_at_exec ) {
zend_hash_clean( Z_ARRVAL( stmt->param_streams ));
}
}
catch( core::CoreException& e ) {
// if the statement executed but failed in a subsequent operation before returning,
// we need to cancel the statement
if( stmt->executed ) {
// we need to cancel the statement and deref the output and stream parameters
if ( stmt->send_streams_at_exec ) {
zend_hash_clean( Z_ARRVAL( stmt->output_params ));
zend_hash_clean( Z_ARRVAL( stmt->param_streams ));
}
if( stmt->executed ) {
SQLCancel( stmt->handle() );
// stmt->executed = false; should this be reset if something fails?
}
@ -742,7 +733,7 @@ void core_sqlsrv_execute( sqlsrv_stmt* stmt TSRMLS_DC, const char* sql, int sql_
// Nothing, exception thrown if an error. stmt->past_fetch_end is set to true if the
// user scrolls past a non-scrollable result set
bool core_sqlsrv_fetch( sqlsrv_stmt* stmt, SQLSMALLINT fetch_orientation, SQLLEN fetch_offset TSRMLS_DC )
bool core_sqlsrv_fetch( sqlsrv_stmt* stmt, SQLSMALLINT fetch_orientation, SQLULEN fetch_offset TSRMLS_DC )
{
// pre-condition check
SQLSRV_ASSERT( fetch_orientation >= SQL_FETCH_NEXT || fetch_orientation <= SQL_FETCH_RELATIVE,
@ -751,7 +742,7 @@ bool core_sqlsrv_fetch( sqlsrv_stmt* stmt, SQLSMALLINT fetch_orientation, SQLLEN
try {
// clear the field cache of the previous fetch
zend_hash_clean( Z_ARRVAL_P( stmt->field_cache ));
zend_hash_clean( Z_ARRVAL( stmt->field_cache ));
CHECK_CUSTOM_ERROR( !stmt->executed, stmt, SQLSRV_ERROR_STATEMENT_NOT_EXECUTED ) {
throw core::CoreException();
@ -879,101 +870,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( zend_hash_index_find( Z_ARRVAL_P( stmt->field_cache ), field_index, (void**) &cached ) == SUCCESS ) {
// 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;
}
try {
sqlsrv_phptype sqlsrv_php_type = sqlsrv_php_type_in;
// close the stream to release the resource
close_active_stream(stmt TSRMLS_CC);
SQLLEN sql_field_type = 0;
SQLLEN sql_field_len = 0;
// 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 {
// 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();
}
field_value = sqlsrv_malloc( cached->len, sizeof( char ), 1 );
memcpy_s( field_value, ( cached->len * sizeof( char )), 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 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( zend_hash_index_find( Z_ARRVAL_P( stmt->field_cache ), i, (void**) &cached ) == FAILURE,
"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;
}
}
}
sqlsrv_phptype sqlsrv_php_type = sqlsrv_php_type_in;
// 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 );
SQLLEN sql_field_type = 0;
SQLLEN sql_field_len = 0;
// Get the corresponding php type from the sql type.
sqlsrv_php_type = stmt->sql_type_to_php_type( sql_field_type, sql_field_len, prefer_string );
}
// 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();
}
// 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 );
// 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;
}
}
}
// Retrieve the data
core_get_field_common( stmt, field_index, sqlsrv_php_type, field_value, field_len TSRMLS_CC );
// 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 ) {
// 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( *stmt, Z_ARRVAL_P( stmt->field_cache ), field_index, &cache,
sizeof( field_cache ) 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 );
catch( core::CoreException& e) {
throw e;
}
// 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
@ -1025,7 +1015,7 @@ void core_sqlsrv_next_result( sqlsrv_stmt* stmt TSRMLS_DC, bool finalize_output_
if( r == SQL_NO_DATA ) {
if( stmt->output_params && finalize_output_params ) {
if( &(stmt->output_params) && finalize_output_params ) {
// if we're finished processing result sets, handle the output parameters
finalize_output_parameters( stmt TSRMLS_CC );
}
@ -1054,28 +1044,28 @@ void core_sqlsrv_next_result( sqlsrv_stmt* stmt TSRMLS_DC, bool finalize_output_
// Returns:
// Nothing, exception thrown if problem occurs
void core_sqlsrv_post_param( sqlsrv_stmt* stmt, unsigned int param_num, zval* param_z TSRMLS_DC )
void core_sqlsrv_post_param( sqlsrv_stmt* stmt, zend_ulong param_num, zval* param_z TSRMLS_DC )
{
SQLSRV_ASSERT( Z_TYPE_P( stmt->param_input_strings ) == IS_ARRAY, "Statement input parameter UTF-16 buffers array invalid." );
SQLSRV_ASSERT( Z_TYPE_P( stmt->param_streams ) == IS_ARRAY, "Statement input parameter streams array invalid." );
SQLSRV_ASSERT( Z_TYPE( stmt->param_input_strings ) == IS_ARRAY, "Statement input parameter UTF-16 buffers array invalid." );
SQLSRV_ASSERT( Z_TYPE( stmt->param_streams ) == IS_ARRAY, "Statement input parameter streams array invalid." );
// if the parameter was an input string, delete it from the array holding input parameter strings
if( zend_hash_index_exists( Z_ARRVAL_P( stmt->param_input_strings ), param_num )) {
core::sqlsrv_zend_hash_index_del( *stmt, Z_ARRVAL_P( stmt->param_input_strings ), param_num TSRMLS_CC );
if( zend_hash_index_exists( Z_ARRVAL( stmt->param_input_strings ), param_num )) {
core::sqlsrv_zend_hash_index_del( *stmt, Z_ARRVAL( stmt->param_input_strings ), param_num TSRMLS_CC );
}
// if the parameter was an input stream, decrement our reference to it and delete it from the array holding input streams
// PDO doesn't need the reference count, but sqlsrv does since the stream can be live after sqlsrv_execute by sending it
// with sqlsrv_send_stream_data.
if( zend_hash_index_exists( Z_ARRVAL_P( stmt->param_streams ), param_num )) {
sqlsrv_stream* stream_encoding;
zend_hash_index_find( Z_ARRVAL_P( stmt->param_streams ), param_num, (void**) &stream_encoding );
core::sqlsrv_zend_hash_index_del( *stmt, Z_ARRVAL_P( stmt->param_streams ), param_num TSRMLS_CC );
if( zend_hash_index_exists( Z_ARRVAL( stmt->param_streams ), param_num )) {
sqlsrv_stream* stream_encoding = NULL;
stream_encoding = reinterpret_cast<sqlsrv_stream*>(zend_hash_index_find_ptr(Z_ARRVAL(stmt->param_streams), param_num));
core::sqlsrv_zend_hash_index_del( *stmt, Z_ARRVAL( stmt->param_streams ), param_num TSRMLS_CC );
}
}
//Calls SQLSetStmtAttr to set a cursor.
void core_sqlsrv_set_scrollable( sqlsrv_stmt* stmt, unsigned int cursor_type TSRMLS_DC )
void core_sqlsrv_set_scrollable( sqlsrv_stmt* stmt, unsigned long cursor_type TSRMLS_DC )
{
try {
@ -1129,7 +1119,7 @@ void core_sqlsrv_set_buffered_query_limit( sqlsrv_stmt* stmt, zval* value_z TSRM
core_sqlsrv_set_buffered_query_limit( stmt, Z_LVAL_P( value_z ) TSRMLS_CC );
}
void core_sqlsrv_set_buffered_query_limit( sqlsrv_stmt* stmt, long limit TSRMLS_DC )
void core_sqlsrv_set_buffered_query_limit( sqlsrv_stmt* stmt, SQLLEN limit TSRMLS_DC )
{
if( limit <= 0 ) {
@ -1154,7 +1144,7 @@ void core_sqlsrv_set_query_timeout( sqlsrv_stmt* stmt, zval* value_z TSRMLS_DC )
THROW_CORE_ERROR( stmt, SQLSRV_ERROR_INVALID_QUERY_TIMEOUT_VALUE, Z_STRVAL_P( value_z ) );
}
core_sqlsrv_set_query_timeout( stmt, Z_LVAL_P( value_z ) TSRMLS_CC );
core_sqlsrv_set_query_timeout( stmt, static_cast<long>( Z_LVAL_P( value_z )) TSRMLS_CC );
}
catch( core::CoreException& ) {
throw;
@ -1169,7 +1159,7 @@ void core_sqlsrv_set_query_timeout( sqlsrv_stmt* stmt, long timeout TSRMLS_DC )
DEBUG_SQLSRV_ASSERT( timeout >= 0 , "core_sqlsrv_set_query_timeout: The value of query timeout cannot be less than 0." );
// set the statement attribute
core::SQLSetStmtAttr( stmt, SQL_ATTR_QUERY_TIMEOUT, reinterpret_cast<SQLPOINTER>( timeout ), SQL_IS_UINTEGER TSRMLS_CC );
core::SQLSetStmtAttr( stmt, SQL_ATTR_QUERY_TIMEOUT, reinterpret_cast<SQLPOINTER>( (SQLLEN)timeout ), SQL_IS_UINTEGER TSRMLS_CC );
// a query timeout of 0 indicates "no timeout", which means that lock_timeout should also be set to "no timeout" which
// is represented by -1.
@ -1232,7 +1222,7 @@ bool core_sqlsrv_send_stream_packet( sqlsrv_stmt* stmt TSRMLS_DC )
// get the stream from the zval we bound
php_stream* param_stream = NULL;
core::sqlsrv_php_stream_from_zval_no_verify( *stmt, param_stream, &stmt->current_stream.stream_z TSRMLS_CC );
core::sqlsrv_php_stream_from_zval_no_verify( *stmt, param_stream, stmt->current_stream.stream_z TSRMLS_CC );
// if we're at the end, then release our current parameter
if( php_stream_eof( param_stream )) {
@ -1248,9 +1238,16 @@ bool core_sqlsrv_send_stream_packet( sqlsrv_stmt* stmt TSRMLS_DC )
// read the data from the stream, send it via SQLPutData and track how much we've sent.
else {
char buffer[ PHP_STREAM_BUFFER_SIZE + 1 ];
size_t buffer_size = sizeof( buffer ) - 3; // -3 to preserve enough space for a cut off UTF-8 character
size_t read = php_stream_read( param_stream, buffer, buffer_size );
stmt->current_stream_read += read;
std::size_t buffer_size = sizeof( buffer ) - 3; // -3 to preserve enough space for a cut off UTF-8 character
std::size_t read = php_stream_read( param_stream, buffer, buffer_size );
if (read > UINT_MAX)
{
LOG(SEV_ERROR, "PHP stream: buffer length exceeded.");
throw core::CoreException();
}
stmt->current_stream_read += static_cast<unsigned int>( read );
if( read > 0 ) {
// if this is a UTF-8 stream, then we will use the UTF-8 encoding to determine if we're in the middle of a character
// then read in the appropriate number more bytes and then retest the string. This way we try at most to convert it
@ -1264,7 +1261,7 @@ bool core_sqlsrv_send_stream_packet( sqlsrv_stmt* stmt TSRMLS_DC )
wchar_t wbuffer[ PHP_STREAM_BUFFER_SIZE + 1 ];
// buffer_size is the # of wchars. Since it set to stmt->param_buffer_size / 2, this is accurate
int wsize = MultiByteToWideChar( stmt->current_stream.encoding, MB_ERR_INVALID_CHARS,
buffer, read, wbuffer, sizeof( wbuffer ) / sizeof( wchar_t ));
buffer, static_cast<int>( read ), wbuffer, static_cast<int>( sizeof( wbuffer ) / sizeof( wchar_t )));
if( wsize == 0 && GetLastError() == ERROR_NO_UNICODE_TRANSLATION ) {
// this will calculate how many bytes were cut off from the last UTF-8 character and read that many more
@ -1280,7 +1277,7 @@ bool core_sqlsrv_send_stream_packet( sqlsrv_stmt* stmt TSRMLS_DC )
}
// try the conversion again with the complete character
wsize = MultiByteToWideChar( stmt->current_stream.encoding, MB_ERR_INVALID_CHARS,
buffer, read + new_read, wbuffer, sizeof( wbuffer ) / sizeof( wchar_t ));
buffer, static_cast<int>( read + new_read ), wbuffer, static_cast<int>( sizeof( wbuffer ) / sizeof( wchar_t )));
// something else must be wrong if it failed
CHECK_CUSTOM_ERROR( wsize == 0, stmt, SQLSRV_ERROR_INPUT_STREAM_ENCODING_TRANSLATE,
get_last_error_message( ERROR_NO_UNICODE_TRANSLATION )) {
@ -1308,7 +1305,6 @@ bool core_sqlsrv_send_stream_packet( sqlsrv_stmt* stmt TSRMLS_DC )
return true;
}
void stmt_option_functor::operator()( sqlsrv_stmt* /*stmt*/, stmt_option const* /*opt*/, zval* /*value_z*/ TSRMLS_DC )
{
TSRMLS_C;
@ -1335,23 +1331,23 @@ void stmt_option_buffered_query_limit:: operator()( sqlsrv_stmt* stmt, stmt_opti
// internal function to release the active stream. Called by each main API function
// that will alter the statement and cancel any retrieval of data from a stream.
void close_active_stream( __inout sqlsrv_stmt* stmt TSRMLS_DC )
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." );
}
@ -1359,7 +1355,7 @@ void close_active_stream( __inout sqlsrv_stmt* stmt TSRMLS_DC )
namespace {
bool is_streamable_type( SQLINTEGER sql_type )
bool is_streamable_type( SQLLEN sql_type )
{
switch( sql_type ) {
case SQL_CHAR:
@ -1378,7 +1374,7 @@ bool is_streamable_type( SQLINTEGER sql_type )
return false;
}
void calc_string_size( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, SQLLEN sql_type, __out SQLLEN& size TSRMLS_DC )
void calc_string_size( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, SQLLEN sql_type, _Out_ SQLLEN& size TSRMLS_DC )
{
try {
@ -1474,200 +1470,204 @@ 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 )
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 {
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_auto_ptr field_value_temp_z;
zval_auto_ptr return_value_z;
zval_auto_ptr function_z;
zval* params[1];
ALLOC_INIT_ZVAL( field_value_temp_z );
ALLOC_INIT_ZVAL( function_z );
ALLOC_INIT_ZVAL( return_value_z );
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 );
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 );
CHECK_SQL_ERROR_OR_WARNING( 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 == 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
ZVAL_STRINGL( field_value_temp_z, field_value_temp, *field_len, 1 );
ZVAL_STRINGL( function_z, "date_create", sizeof("date_create") -1, 1 );
params[0] = field_value_temp_z;
if( *field_len == SQL_NULL_DATA ) {
field_value = NULL;
break;
}
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 = field_value_temp;
field_value_temp.transferred();
break;
}
*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;
php_stream* stream = NULL;
sqlsrv_stream* ss = NULL;
ALLOC_INIT_ZVAL( return_value_z );
SQLINTEGER sql_type;
case SQLSRV_PHPTYPE_STRING:
{
get_field_as_string( stmt, field_index, sqlsrv_php_type, field_value, field_len TSRMLS_CC );
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 );
// 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;
CHECK_CUSTOM_ERROR( !stream, stmt, SQLSRV_ERROR_STREAM_CREATE ) {
throw core::CoreException();
}
ZVAL_UNDEF( &field_value_temp_z );
ZVAL_UNDEF( &function_z );
ZVAL_UNDEF( params );
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 );
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 );
// turn our stream into a zval to be returned
php_stream_to_zval( stream, return_value_z );
CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) {
throw core::CoreException();
}
// 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();
zval_auto_ptr return_value_z;
return_value_z = ( zval * )sqlsrv_malloc( sizeof( zval ));
ZVAL_UNDEF( return_value_z );
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;
}
case SQLSRV_PHPTYPE_NULL:
*field_value = NULL;
*field_len = 0;
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;
default:
DIE( "core_get_field_common: Unexpected sqlsrv_phptype provided" );
break;
}
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);
}
// 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;
}
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:
{
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 );
zval_auto_ptr return_value_z;
return_value_z = ( zval * )sqlsrv_malloc( sizeof( zval ));
ZVAL_UNDEF( return_value_z );
// 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;
}
}
// check_for_next_stream_parameter
// see if there is another stream to be sent. Returns true and sets the stream as current in the statement structure, otherwise
// returns false
bool check_for_next_stream_parameter( __inout sqlsrv_stmt* stmt TSRMLS_DC )
bool check_for_next_stream_parameter( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC )
{
int stream_index = 0;
SQLRETURN r = SQL_SUCCESS;
sqlsrv_stream* stream_encoding;
sqlsrv_stream* stream_encoding = NULL;
zval* param_z = NULL;
// get the index into the streams_ht from the parameter data we set in core_sqlsrv_bind_param
@ -1679,11 +1679,11 @@ bool check_for_next_stream_parameter( __inout sqlsrv_stmt* stmt TSRMLS_DC )
return false;
}
HashTable* streams_ht = Z_ARRVAL_P( stmt->param_streams );
HashTable* streams_ht = Z_ARRVAL( stmt->param_streams );
// pull out the sqlsrv_encoding struct
int zr = zend_hash_index_find( streams_ht, stream_index, (void**) &stream_encoding );
SQLSRV_ASSERT( zr == SUCCESS, "Stream parameter does not exist" ); // if the index isn't in the hash, that's a serious error
stream_encoding = reinterpret_cast<sqlsrv_stream*>(zend_hash_index_find_ptr(streams_ht, stream_index));
SQLSRV_ASSERT(stream_encoding != NULL, "Stream parameter does not exist"); // if the index isn't in the hash, that's a serious error
param_z = stream_encoding->stream_z;
@ -1704,18 +1704,26 @@ bool convert_input_param_to_utf16( zval* input_param_z, zval* converted_param_z
"convert_input_param_z called with invalid parameter states" );
const char* buffer = Z_STRVAL_P( input_param_z );
int buffer_len = Z_STRLEN_P( input_param_z );
std::size_t buffer_len = Z_STRLEN_P( input_param_z );
int wchar_size;
if (buffer_len > INT_MAX)
{
LOG(SEV_ERROR, "Convert input parameter to utf16: buffer length exceeded.");
throw core::CoreException();
}
// if the string is empty, then just return that the conversion succeeded as
// MultiByteToWideChar will "fail" on an empty string.
if( buffer_len == 0 ) {
ZVAL_STRINGL( converted_param_z, "", 0, 1 );
core::sqlsrv_zval_stringl( converted_param_z, "", 0 );
return true;
}
// if the parameter is an input parameter, calc the size of the necessary buffer from the length of the string
wchar_size = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, reinterpret_cast<LPCSTR>( buffer ), buffer_len, NULL, 0 );
wchar_size = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS,
reinterpret_cast<LPCSTR>( buffer ), static_cast<int>( buffer_len ), NULL, 0 );
// if there was a problem determining the size of the string, return false
if( wchar_size == 0 ) {
return false;
@ -1724,7 +1732,7 @@ bool convert_input_param_to_utf16( zval* input_param_z, zval* converted_param_z
wbuffer = reinterpret_cast<wchar_t*>( sqlsrv_malloc( (wchar_size + 1) * sizeof( wchar_t ) ));
// convert the utf-8 string to a wchar string in the new buffer
int r = MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, reinterpret_cast<LPCSTR>( buffer ),
buffer_len, wbuffer, wchar_size );
static_cast<int>( buffer_len ), wbuffer, wchar_size );
// if there was a problem converting the string, then free the memory and return false
if( r == 0 ) {
return false;
@ -1732,8 +1740,9 @@ bool convert_input_param_to_utf16( zval* input_param_z, zval* converted_param_z
// null terminate the string, set the size within the zval, and return success
wbuffer[ wchar_size ] = L'\0';
ZVAL_STRINGL( converted_param_z, reinterpret_cast<char*>( wbuffer.get() ),
wchar_size * sizeof( wchar_t ), 0 );
core::sqlsrv_zval_stringl( converted_param_z, reinterpret_cast<char*>( wbuffer.get() ),
wchar_size * sizeof( wchar_t ) );
sqlsrv_free(wbuffer);
wbuffer.transferred();
return true;
@ -1741,7 +1750,7 @@ bool convert_input_param_to_utf16( zval* input_param_z, zval* converted_param_z
// returns the ODBC C type constant that matches the PHP type and encoding given
SQLSMALLINT default_c_type( sqlsrv_stmt* stmt, unsigned int paramno, zval const* param_z, SQLSRV_ENCODING encoding TSRMLS_DC )
SQLSMALLINT default_c_type( sqlsrv_stmt* stmt, SQLULEN paramno, zval const* param_z, SQLSRV_ENCODING encoding TSRMLS_DC )
{
SQLSMALLINT sql_c_type = SQL_UNKNOWN_TYPE;
int php_type = Z_TYPE_P( param_z );
@ -1762,9 +1771,18 @@ SQLSMALLINT default_c_type( sqlsrv_stmt* stmt, unsigned int paramno, zval const*
break;
}
break;
case IS_BOOL:
case IS_TRUE:
case IS_FALSE:
case IS_LONG:
sql_c_type = SQL_C_LONG;
//ODBC 64-bit long and integer type are 4 byte values.
if ( ( Z_LVAL_P( param_z ) < INT_MIN ) || ( Z_LVAL_P( param_z ) > INT_MAX ) )
{
sql_c_type = SQL_C_SBIGINT;
}
else
{
sql_c_type = SQL_C_SLONG;
}
break;
case IS_DOUBLE:
sql_c_type = SQL_C_DOUBLE;
@ -1803,12 +1821,11 @@ SQLSMALLINT default_c_type( sqlsrv_stmt* stmt, unsigned int paramno, zval const*
// given a zval and encoding, determine the appropriate sql type
void default_sql_type( sqlsrv_stmt* stmt, unsigned int paramno, zval* param_z, SQLSRV_ENCODING encoding,
__out SQLSMALLINT& sql_type TSRMLS_DC )
void default_sql_type( sqlsrv_stmt* stmt, SQLULEN paramno, zval* param_z, SQLSRV_ENCODING encoding,
_Out_ SQLSMALLINT& sql_type TSRMLS_DC )
{
sql_type = SQL_UNKNOWN_TYPE;
int php_type = Z_TYPE_P( param_z );
int php_type = Z_TYPE_P(param_z);
switch( php_type ) {
case IS_NULL:
@ -1825,9 +1842,19 @@ void default_sql_type( sqlsrv_stmt* stmt, unsigned int paramno, zval* param_z, S
break;
}
break;
case IS_BOOL:
case IS_TRUE:
case IS_FALSE:
case IS_LONG:
sql_type = SQL_INTEGER;
//ODBC 64-bit long and integer type are 4 byte values.
if ( ( Z_LVAL_P( param_z ) < INT_MIN ) || ( Z_LVAL_P( param_z ) > INT_MAX ) )
{
sql_type = SQL_BIGINT;
}
else
{
sql_type = SQL_INTEGER;
}
break;
case IS_DOUBLE:
sql_type = SQL_FLOAT;
@ -1874,7 +1901,7 @@ void default_sql_type( sqlsrv_stmt* stmt, unsigned int paramno, zval* param_z, S
// given a zval and encoding, determine the appropriate column size, and decimal scale (if appropriate)
void default_sql_size_and_scale( sqlsrv_stmt* stmt, unsigned int paramno, zval* param_z, SQLSRV_ENCODING encoding,
__out SQLULEN& column_size, __out SQLSMALLINT& decimal_digits TSRMLS_DC )
_Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits TSRMLS_DC )
{
int php_type = Z_TYPE_P( param_z );
column_size = 0;
@ -1886,19 +1913,21 @@ void default_sql_size_and_scale( sqlsrv_stmt* stmt, unsigned int paramno, zval*
column_size = 1;
break;
// size is not necessary for these types, they are inferred by ODBC
case IS_BOOL:
case IS_TRUE:
case IS_FALSE:
case IS_LONG:
case IS_DOUBLE:
case IS_RESOURCE:
break;
case IS_STRING:
{
SQLULEN byte_len = Z_STRLEN_P( param_z ) * ((encoding == SQLSRV_ENCODING_UTF8) ? sizeof( wchar_t ) : sizeof( char ));
size_t char_size = ( encoding == SQLSRV_ENCODING_UTF8 ) ? sizeof( wchar_t ) : sizeof( char );
SQLULEN byte_len = Z_STRLEN_P( param_z ) * char_size;
if( byte_len > SQL_SERVER_MAX_FIELD_SIZE ) {
column_size = SQL_SERVER_MAX_TYPE_SIZE;
}
else {
column_size = Z_STRLEN_P( param_z );
column_size = SQL_SERVER_MAX_FIELD_SIZE / char_size;
}
break;
}
@ -1924,13 +1953,14 @@ void default_sql_size_and_scale( sqlsrv_stmt* stmt, unsigned int paramno, zval*
}
}
void field_cache_dtor( void* data )
void field_cache_dtor( zval* data_z )
{
field_cache* cache = reinterpret_cast<field_cache*>( data );
field_cache* cache = static_cast<field_cache*>( Z_PTR_P( data_z ));
if( cache->value )
{
sqlsrv_free( cache->value );
}
sqlsrv_free( cache );
}
@ -1942,325 +1972,329 @@ void field_cache_dtor( void* data )
void finalize_output_parameters( sqlsrv_stmt* stmt TSRMLS_DC )
{
if( stmt->output_params == NULL )
if( Z_ISUNDEF(stmt->output_params) )
return;
bool converted = true;
HashTable* params_ht = Z_ARRVAL_P( stmt->output_params );
HashTable* params_ht = Z_ARRVAL( stmt->output_params );
zend_ulong index = -1;
zend_string* key = NULL;
void* output_param_temp = NULL;
for( zend_hash_internal_pointer_reset( params_ht );
zend_hash_has_more_elements( params_ht ) == SUCCESS;
zend_hash_move_forward( params_ht ) ) {
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:
{
// adjust the length of the string to the value returned by SQLBindParameter in the ind_ptr parameter
char* str = Z_STRVAL_P( value_z );
SQLLEN str_len = stmt->param_ind_ptrs[ output_param->param_num ];
if( str_len == SQL_NULL_DATA ) {
zend_string_release( Z_STR_P( value_z ));
ZVAL_NULL( value_z );
continue;
}
sqlsrv_output_param *output_param;
core::sqlsrv_zend_hash_get_current_data( *stmt, params_ht, (void**) &output_param TSRMLS_CC );
switch( Z_TYPE_P( output_param->param_z )) {
case IS_STRING:
{
// adjust the length of the string to the value returned by SQLBindParameter in the ind_ptr parameter
char* str = Z_STRVAL_P( output_param->param_z );
SQLLEN str_len = stmt->param_ind_ptrs[ output_param->param_num ];
if( str_len == SQL_NULL_DATA ) {
ZVAL_NULL( output_param->param_z );
continue;
}
// if there was more to output than buffer size to hold it, then throw a truncation error
int null_size = 0;
switch( output_param->encoding ) {
case SQLSRV_ENCODING_UTF8:
null_size = sizeof( wchar_t ); // string isn't yet converted to UTF-8, still UTF-16
break;
case SQLSRV_ENCODING_SYSTEM:
null_size = 1;
break;
case SQLSRV_ENCODING_BINARY:
null_size = 0;
break;
default:
SQLSRV_ASSERT( false, "Invalid encoding in output_param structure." );
break;
}
CHECK_CUSTOM_ERROR( str_len > ( output_param->original_buffer_len - null_size ), stmt,
SQLSRV_ERROR_OUTPUT_PARAM_TRUNCATED, output_param->param_num + 1 ) {
throw core::CoreException();
}
// if it's not in the 8 bit encodings, then it's in UTF-16
if( output_param->encoding != SQLSRV_ENCODING_CHAR && output_param->encoding != SQLSRV_ENCODING_BINARY ) {
bool converted = convert_string_from_utf16_inplace( output_param->encoding, &str, str_len );
CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_OUTPUT_PARAM_ENCODING_TRANSLATE, get_last_error_message()) {
throw core::CoreException();
}
}
else if( output_param->encoding == SQLSRV_ENCODING_BINARY && str_len < output_param->original_buffer_len ) {
// ODBC doesn't null terminate binary encodings, but PHP complains if a string isn't null terminated
// so we do that here if the length of the returned data is less than the original allocation. The
// original allocation null terminates the buffer already.
str[ str_len ] = '\0';
}
// set the string length
ZVAL_STRINGL( output_param->param_z, str, str_len, 0 );
}
// if there was more to output than buffer size to hold it, then throw a truncation error
int null_size = 0;
switch( output_param->encoding ) {
case SQLSRV_ENCODING_UTF8:
null_size = sizeof( wchar_t ); // string isn't yet converted to UTF-8, still UTF-16
break;
case IS_LONG:
// for a long or a float, simply check if NULL was returned and set the parameter to a PHP null if so
if( stmt->param_ind_ptrs[ output_param->param_num ] == SQL_NULL_DATA ) {
ZVAL_NULL( output_param->param_z );
}
else if( output_param->is_bool ) {
convert_to_boolean( output_param->param_z );
}
case SQLSRV_ENCODING_SYSTEM:
null_size = 1;
break;
case IS_DOUBLE:
// for a long or a float, simply check if NULL was returned and set the parameter to a PHP null if so
if( stmt->param_ind_ptrs[ output_param->param_num ] == SQL_NULL_DATA ) {
ZVAL_NULL( output_param->param_z );
}
case SQLSRV_ENCODING_BINARY:
null_size = 0;
break;
default:
DIE( "Illegal or unknown output parameter type. This should have been caught in core_sqlsrv_bind_parameter." );
SQLSRV_ASSERT( false, "Invalid encoding in output_param structure." );
break;
}
CHECK_CUSTOM_ERROR( str_len > ( output_param->original_buffer_len - null_size ), stmt,
SQLSRV_ERROR_OUTPUT_PARAM_TRUNCATED, output_param->param_num + 1 ) {
throw core::CoreException();
}
// if it's not in the 8 bit encodings, then it's in UTF-16
if( output_param->encoding != SQLSRV_ENCODING_CHAR && output_param->encoding != SQLSRV_ENCODING_BINARY ) {
bool converted = convert_zval_string_from_utf16(output_param->encoding, value_z, str_len);
CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_OUTPUT_PARAM_ENCODING_TRANSLATE, get_last_error_message()) {
throw core::CoreException();
}
}
else if( output_param->encoding == SQLSRV_ENCODING_BINARY && str_len < output_param->original_buffer_len ) {
// ODBC doesn't null terminate binary encodings, but PHP complains if a string isn't null terminated
// so we do that here if the length of the returned data is less than the original allocation. The
// original allocation null terminates the buffer already.
str[ str_len ] = '\0';
core::sqlsrv_zval_stringl(value_z, str, str_len);
}
else {
core::sqlsrv_zval_stringl(value_z, str, str_len);
}
}
}
break;
case IS_LONG:
// for a long or a float, simply check if NULL was returned and set the parameter to a PHP null if so
if( stmt->param_ind_ptrs[ output_param->param_num ] == SQL_NULL_DATA ) {
ZVAL_NULL( value_z );
}
else if( output_param->is_bool ) {
convert_to_boolean( value_z );
}
else
{
ZVAL_LONG( value_z, static_cast<int>( Z_LVAL_P( value_z )));
}
break;
case IS_DOUBLE:
// for a long or a float, simply check if NULL was returned and set the parameter to a PHP null if so
if( stmt->param_ind_ptrs[ output_param->param_num ] == SQL_NULL_DATA ) {
ZVAL_NULL( value_z );
}
break;
default:
DIE( "Illegal or unknown output parameter type. This should have been caught in core_sqlsrv_bind_parameter." );
break;
}
value_z = NULL;
} ZEND_HASH_FOREACH_END();
// empty the hash table since it's been processed
zend_hash_clean( Z_ARRVAL_P( stmt->output_params ));
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 == INT_MAX ||
sql_display_size == INT_MAX >> 1 || sql_display_size == UINT_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 ));
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 ) ) {
SQLINTEGER dummy_field_len;
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 );
// 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;
CHECK_CUSTOM_ERROR(( r == SQL_NO_DATA ), stmt, SQLSRV_ERROR_NO_DATA, field_index ) {
throw core::CoreException();
}
do {
if( field_len_temp == SQL_NULL_DATA ) {
field_value = NULL;
sqlsrv_free( field_value_temp );
return;
}
SQLINTEGER 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;
if( r == SQL_SUCCESS_WITH_INFO ) {
// 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 );
SQLCHAR state[ SQL_SQLSTATE_BUFSIZE ];
SQLSMALLINT len;
// 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;
stmt->current_results->get_diag_field( 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len TSRMLS_CC );
if( r == SQL_SUCCESS_WITH_INFO ) {
core::SQLGetDiagField( stmt, 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len
TSRMLS_CC );
}
if( is_truncated_warning( state )) {
} while( r == SQL_SUCCESS_WITH_INFO && is_truncated_warning( state ));
}
else {
SQLLEN dummy_field_len;
// 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 );
// 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 ) {
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;
}
// reset the field_len_temp
field_len_temp = INITIAL_FIELD_STRING_LEN;
} // if( is_truncation_warning ( state ) )
else {
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) {
throw core::CoreException();
}
}
} // if( r == SQL_SUCCESS_WITH_INFO )
do {
SQLLEN initial_field_len = field_len_temp;
// Double the size.
field_len_temp *= 2;
if( sqlsrv_php_type.typeinfo.encoding == SQLSRV_ENCODING_UTF8 ) {
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 ) {
field_len_temp -= initial_field_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_len,
field_len_temp + extra, &dummy_field_len,
false /*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);
}
// 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_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( r == SQL_SUCCESS_WITH_INFO ) {
core::SQLGetDiagField( stmt, 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len
TSRMLS_CC );
}
if( field_len_temp == SQL_NULL_DATA ) {
*field_value = NULL;
sqlsrv_free( field_value_temp );
return;
}
if( sqlsrv_php_type.typeinfo.encoding == CP_UTF8 ) {
} while( r == SQL_SUCCESS_WITH_INFO && is_truncated_warning( state ));
}
else {
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 )
// 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 ));
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';
}
// We have already recieved INITIAL_FIELD_STRING_LEN size data.
field_len_temp -= INITIAL_FIELD_STRING_LEN;
catch( core::CoreException& ) {
// 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 );
*field_value = NULL;
*field_len = 0;
sqlsrv_free( field_value_temp );
throw;
}
catch ( ... ) {
if( dummy_field_len == SQL_NULL_DATA ) {
field_value = NULL;
sqlsrv_free( field_value_temp );
return;
}
*field_value = NULL;
*field_len = 0;
sqlsrv_free( field_value_temp );
throw;
}
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( is_truncation_warning ( state ) )
else {
CHECK_SQL_ERROR_OR_WARNING( r, stmt ) {
throw core::CoreException();
}
}
} // if( r == SQL_SUCCESS_WITH_INFO )
if( sqlsrv_php_type.typeinfo.encoding == SQLSRV_ENCODING_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();
}
}
} // if ( sql_display_size == 0 || sql_display_size == LONG_MAX .. )
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;
}
}
@ -2268,7 +2302,7 @@ void get_field_as_string( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_ph
// return the option from the stmt_opts array that matches the key. If no option found,
// NULL is returned.
stmt_option const* get_stmt_option( sqlsrv_conn const* conn, unsigned long key, const stmt_option stmt_opts[] TSRMLS_DC )
stmt_option const* get_stmt_option( sqlsrv_conn const* conn, zend_ulong key, const stmt_option stmt_opts[] TSRMLS_DC )
{
for( int i = 0; stmt_opts[ i ].key != SQLSRV_STMT_OPTION_INVALID; ++i ) {
@ -2334,13 +2368,12 @@ bool is_valid_sqlsrv_phptype( sqlsrv_phptype type )
// string is place in the stmt->output_params. param_z is modified to hold the new buffer, and buffer, buffer_len and
// stmt->param_ind_ptrs are modified to hold the correct values for SQLBindParameter
void resize_output_buffer_if_necessary( sqlsrv_stmt* stmt, zval* param_z, unsigned int paramno, SQLSRV_ENCODING encoding,
void resize_output_buffer_if_necessary( sqlsrv_stmt* stmt, zval* param_z, SQLULEN paramno, SQLSRV_ENCODING encoding,
SQLSMALLINT c_type, SQLSMALLINT sql_type, SQLULEN column_size, SQLPOINTER& buffer,
SQLLEN& buffer_len TSRMLS_DC )
{
SQLSRV_ASSERT( column_size != SQLSRV_UNKNOWN_SIZE, "column size should be set to a known value." );
buffer_len = Z_STRLEN_P( param_z );
buffer = Z_STRVAL_P( param_z );
SQLLEN expected_len;
SQLLEN buffer_null_extra;
SQLLEN elem_size;
@ -2371,20 +2404,24 @@ void resize_output_buffer_if_necessary( sqlsrv_stmt* stmt, zval* param_z, unsign
// allocate enough space to ALWAYS include the NULL regardless of the type being retrieved since
// we set the last byte(s) to be NULL to avoid the debug build warning from the Zend engine about
// not having a NULL terminator on a string.
buffer = static_cast<char*>( sqlsrv_realloc( buffer, expected_len ));
buffer_len = expected_len; // set the buffer_len to the new allocation size (includes the null terminator taken out below)
zend_string* param_z_string = zend_string_realloc( Z_STR_P(param_z), expected_len, 0 );
// A zval string len doesn't include the null. This calculates the length it should be
// regardless of whether the ODBC type contains the NULL or not.
ZVAL_STRINGL( param_z, reinterpret_cast<char*>( buffer ), without_null_len, 0 );
// null terminate the string to avoid a warning in debug PHP builds
(static_cast<char*>(buffer))[ without_null_len ] = '\0';
}
ZSTR_VAL(param_z_string)[without_null_len] = '\0';
ZVAL_NEW_STR(param_z, param_z_string);
// buffer_len is the length passed to SQLBindParameter. It must contain the space for NULL in the
// buffer when retrieving anything but SQLSRV_ENC_BINARY/SQL_C_BINARY
buffer_len -= buffer_null_extra;
// buffer_len is the length passed to SQLBindParameter. It must contain the space for NULL in the
// buffer when retrieving anything but SQLSRV_ENC_BINARY/SQL_C_BINARY
buffer_len = Z_STRLEN_P(param_z) - buffer_null_extra;
// Zend string length doesn't include the null terminator
ZSTR_LEN(Z_STR_P(param_z)) -= elem_size;
}
buffer = Z_STRVAL_P(param_z);
// The StrLen_Ind_Ptr parameter of SQLBindParameter should contain the length of the data to send, which
// may be less than the size of the buffer since the output may be more than the input. If it is greater,
@ -2394,18 +2431,16 @@ void resize_output_buffer_if_necessary( sqlsrv_stmt* stmt, zval* param_z, unsign
}
}
// output parameters have their reference count incremented so that they do not disappear
// while the query is executed and processed. They are saved in the statement so that
// their reference count may be decremented later (after results are processed)
void save_output_param_for_later( sqlsrv_stmt* stmt, sqlsrv_output_param& param TSRMLS_DC )
{
HashTable* param_ht = Z_ARRVAL_P( stmt->output_params );
int paramno = param.param_num;
core::sqlsrv_zend_hash_index_update( *stmt, param_ht, paramno, &param, sizeof( param )
TSRMLS_CC );
zval_add_ref( &param.param_z ); // we have a reference to the param
HashTable* param_ht = Z_ARRVAL( stmt->output_params );
zend_ulong paramno = static_cast<zend_ulong>( param.param_num );
core::sqlsrv_zend_hash_index_update_mem(*stmt, param_ht, paramno, &param, sizeof( sqlsrv_output_param ));
Z_TRY_ADDREF_P( param.param_z ); // we have a reference to the param
}
@ -2418,19 +2453,19 @@ void send_param_streams( sqlsrv_stmt* stmt TSRMLS_DC )
// called by Zend for each parameter in the sqlsrv_stmt::output_params hash table when it is cleaned/destroyed
void sqlsrv_output_param_dtor( void* data )
void sqlsrv_output_param_dtor( zval* data )
{
sqlsrv_output_param *output_param = reinterpret_cast<sqlsrv_output_param*>( data );
zval_ptr_dtor( &output_param->param_z ); // undo the reference to the string we will no longer hold
sqlsrv_output_param *output_param = static_cast<sqlsrv_output_param*>( Z_PTR_P( data ));
zval_ptr_dtor( output_param->param_z ); // undo the reference to the string we will no longer hold
sqlsrv_free( output_param );
}
// called by Zend for each stream in the sqlsrv_stmt::param_streams hash table when it is cleaned/destroyed
void sqlsrv_stream_dtor( void* data )
void sqlsrv_stream_dtor( zval* data )
{
sqlsrv_stream* stream_encoding = reinterpret_cast<sqlsrv_stream*>( data );
zval_ptr_dtor( &stream_encoding->stream_z ); // undo the reference to the stream we will no longer hold
sqlsrv_stream* stream_encoding = static_cast<sqlsrv_stream*>( Z_PTR_P( data ));
zval_ptr_dtor( stream_encoding->stream_z ); // undo the reference to the stream we will no longer hold
sqlsrv_free( stream_encoding );
}
}

View file

@ -3,7 +3,7 @@
//
// Contents: Implementation of PHP streams for reading SQL Server data
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -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;
@ -48,10 +45,9 @@ int sqlsrv_stream_close( php_stream* stream, int /*close_handle*/ TSRMLS_DC )
// read from a sqlsrv stream into the buffer provided by Zend. The parameters for binary vs. char are
// set when sqlsrv_get_field is called by the user specifying which field type they want.
size_t sqlsrv_stream_read( php_stream* stream, __out_bcount(count) char* buf, size_t count TSRMLS_DC )
size_t sqlsrv_stream_read( php_stream* stream, _Out_writes_bytes_(count) char* buf, size_t count TSRMLS_DC )
{
SQLINTEGER read = 0;
SQLLEN read = 0;
SQLSMALLINT c_type = SQL_C_CHAR;
char* get_data_buffer = buf;
sqlsrv_malloc_auto_ptr<char> temp_buf;
@ -145,21 +141,28 @@ size_t sqlsrv_stream_read( php_stream* stream, __out_bcount(count) char* buf, si
}
// if the encoding is UTF-8
if( c_type == SQL_C_WCHAR ) {
count *= 2; // undo the shift to use the full buffer
if (c_type == SQL_C_WCHAR) {
// flags set to 0 by default, which means that any invalid characters are dropped rather than causing
// an error. This happens only on XP.
DWORD flags = 0;
count *= 2; // undo the shift to use the full buffer
// flags set to 0 by default, which means that any invalid characters are dropped rather than causing
// an error. This happens only on XP.
DWORD flags = 0;
// convert to UTF-8
if (g_osversion.dwMajorVersion >= SQLSRV_OS_VISTA_OR_LATER) {
// Vista (and later) will detect invalid UTF-16 characters and raise an error.
flags = WC_ERR_INVALID_CHARS;
}
if ( count > INT_MAX || (read >> 1) > INT_MAX )
{
LOG(SEV_ERROR, "UTF-16 (wide character) string mapping: buffer length exceeded.");
throw core::CoreException();
}
// convert to UTF-8
if( g_osversion.dwMajorVersion >= SQLSRV_OS_VISTA_OR_LATER ) {
// Vista (and later) will detect invalid UTF-16 characters and raise an error.
flags = WC_ERR_INVALID_CHARS;
}
int enc_len = WideCharToMultiByte( ss->encoding, flags, reinterpret_cast<LPCWSTR>( temp_buf.get() ),
read >> 1, buf, count, NULL, NULL );
static_cast<int>(read >> 1), buf, static_cast<int>(count), NULL, NULL );
if( enc_len == 0 ) {
@ -200,13 +203,8 @@ php_stream_ops sqlsrv_stream_ops = {
// open a stream and return the sqlsrv_stream_ops function table as part of the
// return value. There is only one valid way to open a stream, using sqlsrv_get_field on
// certain field types. A sqlsrv stream may only be opened in read mode.
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION >= 6
static php_stream* sqlsrv_stream_opener( php_stream_wrapper* wrapper, __in const char*, __in const char* mode,
int options, __in char **, php_stream_context* STREAMS_DC TSRMLS_DC )
#else
static php_stream* sqlsrv_stream_opener( php_stream_wrapper* wrapper, __in char*, __in char* mode,
int options, __in char **, php_stream_context* STREAMS_DC TSRMLS_DC )
#endif
static php_stream* sqlsrv_stream_opener( php_stream_wrapper* wrapper, _In_ const char*, _In_ const char* mode,
int options, _In_ zend_string **, php_stream_context* STREAMS_DC TSRMLS_DC )
{
#if ZEND_DEBUG

View file

@ -5,7 +5,7 @@
//
// Comments: Mostly error handling and some type handling
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -33,9 +33,9 @@ SQLCHAR INTERNAL_FORMAT_ERROR[] = "An internal error occurred. FormatMessage fa
char last_err_msg[ 2048 ]; // 2k to hold the error messages
// routine used by utf16_string_from_mbcs_string
unsigned int convert_string_from_default_encoding( unsigned int php_encoding, __in_bcount(mbcs_len) char const* mbcs_in_string,
unsigned int convert_string_from_default_encoding( unsigned int php_encoding, _In_reads_bytes_(mbcs_len) char const* mbcs_in_string,
unsigned int mbcs_len,
__out_ecount(utf16_len) __transfer( mbcs_in_string ) wchar_t* utf16_out_string,
_Out_writes_(utf16_len) __transfer( mbcs_in_string ) wchar_t* utf16_out_string,
unsigned int utf16_len );
}
@ -70,18 +70,19 @@ void core_sqlsrv_register_logger( log_callback driver_logger )
// utf-16 string is released by this function if no errors occurred. Otherwise the parameters are not changed
// and false is returned.
bool convert_string_from_utf16_inplace( SQLSRV_ENCODING encoding, char** string, SQLINTEGER& len)
bool convert_string_from_utf16_inplace( SQLSRV_ENCODING encoding, char** string, SQLLEN& len)
{
SQLSRV_ASSERT( string != NULL && *string != NULL, "String must be specified" );
SQLSRV_ASSERT( string != NULL, "String must be specified" );
// for the empty string, we simply returned we converted it
if( len == 0 && *string[0] == '\0' ) {
return true;
}
if (validate_string(*string, len)) {
return true;
}
char* outString = NULL;
SQLINTEGER outLen = 0;
bool result = convert_string_from_utf16( encoding, reinterpret_cast<const wchar_t*>(*string), len / sizeof(wchar_t), &outString, outLen);
SQLLEN outLen = 0;
bool result = convert_string_from_utf16( encoding,
reinterpret_cast<const wchar_t*>(*string), int(len / sizeof(wchar_t)), &outString, outLen);
if (result)
{
@ -93,7 +94,49 @@ bool convert_string_from_utf16_inplace( SQLSRV_ENCODING encoding, char** string,
return result;
}
bool convert_string_from_utf16( SQLSRV_ENCODING encoding, const wchar_t* inString, SQLINTEGER cchInLen, char** outString, SQLINTEGER& cchOutLen )
bool convert_zval_string_from_utf16(SQLSRV_ENCODING encoding, zval* value_z, SQLLEN& len)
{
char* string = Z_STRVAL_P(value_z);
if (validate_string(string, len)) {
return true;
}
char* outString = NULL;
SQLLEN outLen = 0;
bool result = convert_string_from_utf16(encoding,
reinterpret_cast<const wchar_t*>(string), int(len / sizeof(wchar_t)), &outString, outLen);
if (result)
{
core::sqlsrv_zval_stringl(value_z, outString, outLen);
sqlsrv_free(outString);
len = outLen;
}
return result;
}
bool validate_string(char* string, SQLLEN& len)
{
SQLSRV_ASSERT(string != NULL, "String must be specified");
//for the empty string, we simply returned we converted it
if (len == 0 && string[0] == '\0') {
return true;
}
if ((len / sizeof(wchar_t)) > INT_MAX)
{
LOG(SEV_ERROR, "UTP-16 (wide character) string mapping: buffer length exceeded.");
throw core::CoreException();
}
return false;
}
bool convert_string_from_utf16( SQLSRV_ENCODING encoding, const wchar_t* inString, SQLINTEGER cchInLen, char** outString, SQLLEN& cchOutLen )
{
SQLSRV_ASSERT( inString != NULL, "Input string must be specified" );
SQLSRV_ASSERT( outString != NULL, "Output buffer pointer must be specified" );
@ -126,7 +169,7 @@ bool convert_string_from_utf16( SQLSRV_ENCODING encoding, const wchar_t* inStrin
char* newString = reinterpret_cast<char*>( sqlsrv_malloc( cchOutLen + 1 /* NULL char*/ ));
int rc = WideCharToMultiByte( encoding, flags,
inString, cchInLen,
newString, cchOutLen, NULL, NULL );
newString, static_cast<int>(cchOutLen), NULL, NULL );
if( rc == 0 ) {
cchOutLen = 0;
sqlsrv_free( newString );
@ -139,7 +182,6 @@ bool convert_string_from_utf16( SQLSRV_ENCODING encoding, const wchar_t* inStrin
return true;
}
// thin wrapper around convert_string_from_default_encoding that handles
// allocation of the destination string. An empty string passed in returns
// failure since it's a failure case for convert_string_from_default_encoding.
@ -219,10 +261,10 @@ bool core_sqlsrv_get_odbc_error( sqlsrv_context& ctx, int record_number, sqlsrv_
return false;
}
SQLINTEGER sqlstate_len = 0;
SQLLEN sqlstate_len = 0;
convert_string_from_utf16(enc, wsqlstate, sizeof(wsqlstate), (char**)&error->sqlstate, sqlstate_len);
SQLINTEGER message_len = 0;
SQLLEN message_len = 0;
convert_string_from_utf16(enc, wnative_message, wmessage_len, (char**)&error->native_message, message_len);
break;
}
@ -264,12 +306,12 @@ void core_sqlsrv_format_driver_error( sqlsrv_context& ctx, sqlsrv_error_const co
LOG( severity, "%1!s!: message = %2!s!", ctx.func(), formatted_error->native_message );
}
DWORD core_sqlsrv_format_message( char* output_buffer, unsigned output_len, const char* format, ... )
DWORD core_sqlsrv_format_message( char*& output_buffer, unsigned output_len, const char* format, ... )
{
va_list format_args;
va_start( format_args, format );
DWORD rc = FormatMessage( FORMAT_MESSAGE_FROM_STRING, format, 0, 0, output_buffer, output_len, &format_args );
DWORD rc = FormatMessage( FORMAT_MESSAGE_FROM_STRING, format, 0, 0, static_cast<LPSTR>(output_buffer), SQL_MAX_MESSAGE_LENGTH, &format_args );
va_end( format_args );
@ -328,8 +370,8 @@ namespace {
// returned in utf16_out_string. An empty string passed in will result as
// a failure since MBTWC returns 0 for both an empty string and failure
// to convert.
unsigned int convert_string_from_default_encoding( unsigned int php_encoding, __in_bcount(mbcs_len) char const* mbcs_in_string,
unsigned int mbcs_len, __out_ecount(utf16_len) __transfer( mbcs_in_string ) wchar_t* utf16_out_string,
unsigned int convert_string_from_default_encoding( unsigned int php_encoding, _In_reads_bytes_(mbcs_len) char const* mbcs_in_string,
unsigned int mbcs_len, _Out_writes_(utf16_len) __transfer( mbcs_in_string ) wchar_t* utf16_out_string,
unsigned int utf16_len )
{
unsigned int win_encoding = CP_ACP;

View file

@ -2,7 +2,7 @@
// File: init.cpp
// Contents: initialization routines for the extension
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -17,6 +17,10 @@
//---------------------------------------------------------------------------------------------------------------------------------
#include "php_sqlsrv.h"
#ifdef ZTS
ZEND_TSRMLS_CACHE_DEFINE();
#endif
ZEND_GET_MODULE(g_sqlsrv)
extern "C" {
@ -32,6 +36,10 @@ HashTable* g_ss_warnings_to_ignore_ht = NULL;
// encodings we understand
HashTable* g_ss_encodings_ht = NULL;
// Destructors called by Zend for each element in the hashtable
void sqlsrv_error_const_dtor( zval* element );
void sqlsrv_encoding_dtor( zval* element );
// henv context for creating connections
sqlsrv_context* g_henv_cp;
sqlsrv_context* g_henv_ncp;
@ -256,20 +264,21 @@ zend_module_entry g_sqlsrv_module_entry =
PHP_MINIT_FUNCTION(sqlsrv)
{
SQLSRV_UNUSED( type );
core_sqlsrv_register_logger( ss_sqlsrv_log );
core_sqlsrv_register_logger( ss_sqlsrv_log );
// our global variables are initialized in the RINIT function
#if defined(ZTS)
#if defined(ZTS)
if( ts_allocate_id( &sqlsrv_globals_id,
sizeof( zend_sqlsrv_globals ),
(ts_allocate_ctor) NULL,
(ts_allocate_dtor) NULL ) == 0 )
return FAILURE;
ZEND_TSRMLS_CACHE_UPDATE();
#endif
SQLSRV_STATIC_ASSERT( sizeof( sqlsrv_sqltype ) == sizeof( long ));
SQLSRV_STATIC_ASSERT( sizeof( sqlsrv_phptype ) == sizeof( long ));
SQLSRV_STATIC_ASSERT( sizeof( sqlsrv_sqltype ) == sizeof( zend_long ));
SQLSRV_STATIC_ASSERT( sizeof( sqlsrv_phptype ) == sizeof( zend_long ));
REGISTER_INI_ENTRIES();
@ -401,12 +410,9 @@ PHP_MINIT_FUNCTION(sqlsrv)
try {
// initialize list of warnings to ignore
// initialize list of warnings to ignore
g_ss_warnings_to_ignore_ht = reinterpret_cast<HashTable*>( pemalloc( sizeof( HashTable ), 1 ));
int zr = ::zend_hash_init( g_ss_warnings_to_ignore_ht, 6, NULL, NULL, 1 );
if( zr == FAILURE ) {
throw ss::SSException();
}
zend_hash_init( g_ss_warnings_to_ignore_ht, 6, NULL, sqlsrv_error_const_dtor /*pDestructor*/, 1 );
sqlsrv_error_const error_to_ignore;
@ -415,8 +421,7 @@ PHP_MINIT_FUNCTION(sqlsrv)
error_to_ignore.native_message = NULL;
error_to_ignore.native_code = 5701;
error_to_ignore.format = false;
zr = zend_hash_next_index_insert( g_ss_warnings_to_ignore_ht, &error_to_ignore, sizeof( sqlsrv_error_const ), NULL );
if( zr == FAILURE ) {
if (NULL == zend_hash_next_index_insert_mem( g_ss_warnings_to_ignore_ht, (void*)&error_to_ignore, sizeof(sqlsrv_error_const))) {
throw ss::SSException();
}
@ -425,8 +430,7 @@ PHP_MINIT_FUNCTION(sqlsrv)
error_to_ignore.native_message = NULL;
error_to_ignore.native_code = 5703;
error_to_ignore.format = false;
zr = zend_hash_next_index_insert( g_ss_warnings_to_ignore_ht, &error_to_ignore, sizeof( sqlsrv_error_const ), NULL );
if( zr == FAILURE ) {
if (NULL == zend_hash_next_index_insert_mem( g_ss_warnings_to_ignore_ht, (void*)&error_to_ignore, sizeof(sqlsrv_error_const))) {
throw ss::SSException();
}
@ -435,8 +439,7 @@ PHP_MINIT_FUNCTION(sqlsrv)
error_to_ignore.native_message = NULL;
error_to_ignore.native_code = -1;
error_to_ignore.format = false;
zr = zend_hash_next_index_insert( g_ss_warnings_to_ignore_ht, &error_to_ignore, sizeof( sqlsrv_error_const ), NULL );
if( zr == FAILURE ) {
if (NULL == zend_hash_next_index_insert_mem( g_ss_warnings_to_ignore_ht, (void*)&error_to_ignore, sizeof(sqlsrv_error_const))) {
throw ss::SSException();
}
@ -445,8 +448,7 @@ PHP_MINIT_FUNCTION(sqlsrv)
error_to_ignore.native_message = NULL;
error_to_ignore.native_code = -1;
error_to_ignore.format = false;
zr = zend_hash_next_index_insert( g_ss_warnings_to_ignore_ht, &error_to_ignore, sizeof( sqlsrv_error_const ), NULL );
if( zr == FAILURE ) {
if (NULL == zend_hash_next_index_insert_mem( g_ss_warnings_to_ignore_ht, (void*)&error_to_ignore, sizeof(sqlsrv_error_const))) {
throw ss::SSException();
}
@ -455,8 +457,7 @@ PHP_MINIT_FUNCTION(sqlsrv)
error_to_ignore.native_message = NULL;
error_to_ignore.native_code = -1;
error_to_ignore.format = false;
zr = zend_hash_next_index_insert( g_ss_warnings_to_ignore_ht, &error_to_ignore, sizeof( sqlsrv_error_const ), NULL );
if( zr == FAILURE ) {
if (NULL == zend_hash_next_index_insert_mem( g_ss_warnings_to_ignore_ht, (void*)&error_to_ignore, sizeof(sqlsrv_error_const))) {
throw ss::SSException();
}
@ -465,8 +466,7 @@ PHP_MINIT_FUNCTION(sqlsrv)
error_to_ignore.native_message = NULL;
error_to_ignore.native_code = 40608;
error_to_ignore.format = false;
zr = zend_hash_next_index_insert( g_ss_warnings_to_ignore_ht, &error_to_ignore, sizeof( sqlsrv_error_const ), NULL );
if( zr == FAILURE ) {
if (NULL == zend_hash_next_index_insert_mem( g_ss_warnings_to_ignore_ht, (void*)&error_to_ignore, sizeof(sqlsrv_error_const))) {
throw ss::SSException();
}
@ -475,8 +475,7 @@ PHP_MINIT_FUNCTION(sqlsrv)
error_to_ignore.native_message = NULL;
error_to_ignore.native_code = 9927;
error_to_ignore.format = false;
zr = zend_hash_next_index_insert(g_ss_warnings_to_ignore_ht, &error_to_ignore, sizeof(sqlsrv_error_const), NULL);
if (zr == FAILURE) {
if (NULL == zend_hash_next_index_insert_mem(g_ss_warnings_to_ignore_ht, (void*)&error_to_ignore, sizeof(sqlsrv_error_const))) {
throw ss::SSException();
}
@ -491,26 +490,20 @@ PHP_MINIT_FUNCTION(sqlsrv)
// supported encodings
g_ss_encodings_ht = reinterpret_cast<HashTable*>( pemalloc( sizeof( HashTable ), 1 ));
int zr = zend_hash_init( g_ss_encodings_ht, 3, NULL /*use standard hash function*/, NULL /*no resource destructor*/, 1 /*persistent*/ );
if( zr == FAILURE ) {
throw ss::SSException();
}
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 );
zr = zend_hash_next_index_insert( g_ss_encodings_ht, &sql_enc_char, sizeof( sqlsrv_encoding ), NULL /*no pointer to the new value necessasry*/ );
if( zr == FAILURE ) {
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 );
zr = zend_hash_next_index_insert( g_ss_encodings_ht, &sql_enc_bin, sizeof( sqlsrv_encoding ), NULL /*no pointer to the new value necessasry*/ );
if( zr == FAILURE ) {
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 );
zr = zend_hash_next_index_insert( g_ss_encodings_ht, &sql_enc_utf8, sizeof( sqlsrv_encoding ), NULL /*no pointer to the new value necessasry*/ );
if( zr == FAILURE ) {
if (NULL == zend_hash_next_index_insert_mem( g_ss_encodings_ht, (void*)&sql_enc_utf8, sizeof( sqlsrv_encoding ))) {
throw ss::SSException();
}
}
@ -522,18 +515,11 @@ PHP_MINIT_FUNCTION(sqlsrv)
// initialize list of sqlsrv errors
g_ss_errors_ht = reinterpret_cast<HashTable*>( pemalloc( sizeof( HashTable ), 1 ));
int zr = ::zend_hash_init( g_ss_errors_ht, 50, NULL, NULL, 1 );
if( zr == FAILURE ) {
LOG( SEV_ERROR, "%1!s!: Failed to initialize the sqlsrv errors hashtable.", _FN_ );
return FAILURE;
}
::zend_hash_init( g_ss_errors_ht, 50, NULL, sqlsrv_error_const_dtor /*pDestructor*/, 1 );
for( int i = 0; SS_ERRORS[ i ].error_code != -1; ++i ) {
zr = ::zend_hash_index_update( g_ss_errors_ht, SS_ERRORS[ i ].error_code,
&( SS_ERRORS[ i ].sqlsrv_error ), sizeof( SS_ERRORS[ i ].sqlsrv_error ), NULL );
if( zr == FAILURE ) {
for( int i = 0; SS_ERRORS[ i ].error_code != UINT_MAX; ++i ) {
if (NULL == ::zend_hash_index_update_mem( g_ss_errors_ht, SS_ERRORS[ i ].error_code,
&( SS_ERRORS[ i ].sqlsrv_error ), sizeof( SS_ERRORS[ i ].sqlsrv_error ))) {
LOG( SEV_ERROR, "%1!s!: Failed to insert data into sqlsrv errors hashtable.", _FN_ );
return FAILURE;
}
@ -545,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 );
}
@ -563,6 +548,18 @@ PHP_MINIT_FUNCTION(sqlsrv)
return SUCCESS;
}
// 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 = 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 = static_cast<sqlsrv_encoding*>( Z_PTR_P(elem) );
pefree(sql_enc, 1);
}
// Module shutdown function
// Free the environment handles allocated in MINIT and unregister our stream wrapper.
@ -572,7 +569,7 @@ PHP_MINIT_FUNCTION(sqlsrv)
PHP_MSHUTDOWN_FUNCTION(sqlsrv)
{
SQLSRV_UNUSED( type );
UNREGISTER_INI_ENTRIES();
// clean up the list of sqlsrv errors
@ -607,12 +604,14 @@ PHP_RINIT_FUNCTION(sqlsrv)
{
SQLSRV_UNUSED( module_number );
SQLSRV_UNUSED( type );
#if defined(ZTS)
ZEND_TSRMLS_CACHE_UPDATE();
#endif
SQLSRV_G( warnings_return_as_errors ) = true;
ALLOC_INIT_ZVAL( SQLSRV_G( errors ));
Z_SET_ISREF_P( SQLSRV_G( errors ));
ALLOC_INIT_ZVAL( SQLSRV_G( warnings ));
Z_SET_ISREF_P( SQLSRV_G( warnings ));
ZVAL_NULL( &SQLSRV_G( errors ));
ZVAL_NULL( &SQLSRV_G( warnings ));
LOG_FUNCTION( "PHP_RINIT for php_sqlsrv" );
@ -627,8 +626,6 @@ PHP_RINIT_FUNCTION(sqlsrv)
LOG( SEV_NOTICE, INI_PREFIX INI_LOG_SUBSYSTEMS " = %1!d!", SQLSRV_G( log_subsystems ));
LOG( SEV_NOTICE, INI_PREFIX INI_BUFFERED_QUERY_LIMIT " = %1!d!", SQLSRV_G( buffered_query_limit ));
// verify memory at the end of the request (in debug mode only)
full_mem_check(MEMCHECK_SILENT);
return SUCCESS;
}
@ -645,24 +642,19 @@ PHP_RSHUTDOWN_FUNCTION(sqlsrv)
LOG_FUNCTION( "PHP_RSHUTDOWN for php_sqlsrv" );
reset_errors( TSRMLS_C );
// TODO - destruction
zval_ptr_dtor( &SQLSRV_G( errors ));
zval_ptr_dtor( &SQLSRV_G( warnings ));
// verify memory at the end of the request (in debug mode only)
full_mem_check(MEMCHECK_SILENT);
return SUCCESS;
}
// Called for php_info(); Displays the INI settings registered and their current values
PHP_MINFO_FUNCTION(sqlsrv)
{
#if defined(ZTS)
SQLSRV_UNUSED( tsrm_ls );
#endif
php_info_print_table_start();
php_info_print_table_header(2, "sqlsrv support", "enabled");
php_info_print_table_row(2, "ExtensionVer", VER_FILEVERSION_STR);
php_info_print_table_end();
DISPLAY_INI_ENTRIES();
}

View file

@ -888,7 +888,7 @@ 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_setbulkmode (HDBC, INT, _In_reads_bytes_(cbField) void*, INT cbField, _In_reads_bytes_(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);
@ -958,7 +958,7 @@ HANDLE __stdcall OpenSqlFilestream (
LPCWSTR FilestreamPath,
SQL_FILESTREAM_DESIRED_ACCESS DesiredAccess,
ULONG OpenOptions,
__in_bcount(FilestreamTransactionContextLength)
_In_reads_bytes_(FilestreamTransactionContextLength)
LPBYTE FilestreamTransactionContext,
SSIZE_T FilestreamTransactionContextLength,
PLARGE_INTEGER AllocationSize);
@ -995,11 +995,11 @@ extern "C" {
// 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,
_In_z_ PCWSTR wszVersion,
// I the instance name
__in_z PCWSTR pInstanceName,
_In_z_ PCWSTR pInstanceName,
// I reserved for the future use. Currently should be set to 0.
__in DWORD dwFlags
_In_ DWORD dwFlags
);
// type definition for pointer to LocalDBCreateInstance function
@ -1008,14 +1008,14 @@ typedef FnLocalDBCreateInstance* PFnLocalDBCreateInstance;
// type definition for LocalDBStartInstance function
typedef HRESULT __cdecl FnLocalDBStartInstance (
// I the LocalDB instance name
__in_z PCWSTR pInstanceName,
_In_z_ PCWSTR pInstanceName,
// I reserved for the future use. Currently should be set to 0.
__in DWORD dwFlags,
_In_ DWORD dwFlags,
// O the buffer to store the connection string to the LocalDB instance
__out_ecount_z_opt(*lpcchSqlConnection) LPWSTR wszSqlConnection,
_Out_writes_opt_z_(*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
_Inout_opt_ LPDWORD lpcchSqlConnection
);
// type definition for pointer to LocalDBStartInstance function
@ -1027,19 +1027,19 @@ typedef FnLocalDBStartInstance* PFnLocalDBStartInstance;
// type definition for LocalDBFormatMessage function
typedef HRESULT __cdecl FnLocalDBFormatMessage(
// I the LocalDB error code
__in HRESULT hrLocalDB,
_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,
_In_ DWORD dwFlags,
// I Language desired (LCID) or 0 (in which case Win32 FormatMessage order is used)
__in DWORD dwLanguageId,
_In_ DWORD dwLanguageId,
// O the buffer to store the LocalDB error message
__out_ecount_z(*lpcchMessage) LPWSTR wszMessage,
_Out_writes_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
_Inout_ LPDWORD lpcchMessage
);
// type definition for function pointer to LocalDBFormatMessage function
@ -1112,14 +1112,14 @@ FnLocalDBStartInstance LocalDBStartInstance;
// type definition for LocalDBStopInstance function
typedef HRESULT __cdecl FnLocalDBStopInstance (
// I the LocalDB instance name
__in_z PCWSTR pInstanceName,
_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,
_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
_In_ ULONG ulTimeout
);
// type definition for pointer to LocalDBStopInstance function
@ -1149,9 +1149,9 @@ FnLocalDBStopInstance LocalDBStopInstance;
// type definition for LocalDBDeleteInstance function
typedef HRESULT __cdecl FnLocalDBDeleteInstance (
// I the LocalDB instance name
__in_z PCWSTR pInstanceName,
_In_z_ PCWSTR pInstanceName,
// I reserved for the future use. Currently should be set to 0.
__in DWORD dwFlags
_In_ DWORD dwFlags
);
// type definition for pointer to LocalDBDeleteInstance function
@ -1202,10 +1202,10 @@ typedef TLocalDBInstanceName* PTLocalDBInstanceName;
// type definition for LocalDBGetInstances function
typedef HRESULT __cdecl FnLocalDBGetInstances(
// O buffer for a LocalDB instance names
__out PTLocalDBInstanceName pInstanceNames,
_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
_Inout_ LPDWORD lpdwNumberOfInstances
);
// type definition for pointer to LocalDBGetInstances function
@ -1267,11 +1267,11 @@ typedef LocalDBInstanceInfo* PLocalDBInstanceInfo;
// type definition for LocalDBGetInstanceInfo function
typedef HRESULT __cdecl FnLocalDBGetInstanceInfo(
// I the LocalDB instance name
__in_z PCWSTR wszInstanceName,
_In_z_ PCWSTR wszInstanceName,
// O instance information
__out PLocalDBInstanceInfo pInfo,
_Out_ PLocalDBInstanceInfo pInfo,
// I Size of LocalDBInstanceInfo structure in bytes
__in DWORD cbInfo);
_In_ DWORD cbInfo);
// type definition for pointer to LocalDBGetInstances function
typedef FnLocalDBGetInstanceInfo* PFnLocalDBGetInstanceInfo;
@ -1298,10 +1298,10 @@ typedef TLocalDBVersion* PTLocalDBVersion;
// type definition for LocalDBGetVersions function
typedef HRESULT __cdecl FnLocalDBGetVersions(
// O buffer for installed LocalDB versions
__out PTLocalDBVersion pVersions,
_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
_Inout_ LPDWORD lpdwNumberOfVersions
);
// type definition for pointer to LocalDBGetVersions function
@ -1352,11 +1352,11 @@ typedef LocalDBVersionInfo* PLocalDBVersionInfo;
// type definition for LocalDBGetVersionInfo function
typedef HRESULT __cdecl FnLocalDBGetVersionInfo(
// I LocalDB version string
__in_z PCWSTR wszVersion,
_In_z_ PCWSTR wszVersion,
// O version information
__out PLocalDBVersionInfo pVersionInfo,
_Out_ PLocalDBVersionInfo pVersionInfo,
// I Size of LocalDBVersionInfo structure in bytes
__in DWORD cbVersionInfo
_In_ DWORD cbVersionInfo
);
// type definition for pointer to LocalDBGetVersionInfo function
@ -1402,13 +1402,13 @@ FnLocalDBStopTracing LocalDBStopTracing;
// type definition for LocalDBShareInstance function
typedef HRESULT __cdecl FnLocalDBShareInstance(
// I the SID of the LocalDB instance owner
__in_opt PSID pOwnerSID,
_In_opt_ PSID pOwnerSID,
// I the private name of LocalDB instance which should be shared
__in_z PCWSTR wszPrivateLocalDBInstanceName,
_In_z_ PCWSTR wszPrivateLocalDBInstanceName,
// I the public shared name
__in_z PCWSTR wszSharedName,
_In_z_ PCWSTR wszSharedName,
// I reserved for the future use. Currently should be set to 0.
__in DWORD dwFlags);
_In_ DWORD dwFlags);
// type definition for pointer to LocalDBShareInstance function
typedef FnLocalDBShareInstance* PFnLocalDBShareInstance;
@ -1426,9 +1426,9 @@ FnLocalDBShareInstance LocalDBShareInstance;
// type definition for LocalDBUnshareInstance function
typedef HRESULT __cdecl FnLocalDBUnshareInstance(
// I the LocalDB instance name
__in_z PCWSTR pInstanceName,
_In_z_ PCWSTR pInstanceName,
// I reserved for the future use. Currently should be set to 0.
__in DWORD dwFlags);
_In_ DWORD dwFlags);
// type definition for pointer to LocalDBUnshareInstance function
typedef FnLocalDBUnshareInstance* PFnLocalDBUnshareInstance;
@ -1653,11 +1653,11 @@ Cleanup:
HRESULT __cdecl
LocalDBCreateInstance (
// I the LocalDB version (e.g. 11.0 or 11.0.1094.2)
__in_z PCWSTR wszVersion,
_In_z_ PCWSTR wszVersion,
// I the instance name
__in_z PCWSTR pInstanceName,
_In_z_ PCWSTR pInstanceName,
// I reserved for the future use. Currently should be set to 0.
__in DWORD dwFlags
_In_ DWORD dwFlags
)
{
LOCALDB_PROXY(LocalDBCreateInstance)(wszVersion, pInstanceName, dwFlags);
@ -1666,14 +1666,14 @@ LocalDBCreateInstance (
HRESULT __cdecl
LocalDBStartInstance(
// I the instance name
__in_z PCWSTR pInstanceName,
_In_z_ PCWSTR pInstanceName,
// I reserved for the future use. Currently should be set to 0.
__in DWORD dwFlags,
_In_ DWORD dwFlags,
// O the buffer to store the connection string to the LocalDB instance
__out_ecount_z_opt(*lpcchSqlConnection) LPWSTR wszSqlConnection,
_Out_writes_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
_Inout_opt_ LPDWORD lpcchSqlConnection
)
{
LOCALDB_PROXY(LocalDBStartInstance)(pInstanceName, dwFlags, wszSqlConnection, lpcchSqlConnection);
@ -1682,14 +1682,14 @@ LocalDBStartInstance(
HRESULT __cdecl
LocalDBStopInstance (
// I the instance name
__in_z PCWSTR pInstanceName,
_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,
_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
_In_ ULONG ulTimeout
)
{
LOCALDB_PROXY(LocalDBStopInstance)(pInstanceName, dwFlags, ulTimeout);
@ -1698,9 +1698,9 @@ LocalDBStopInstance (
HRESULT __cdecl
LocalDBDeleteInstance (
// I the instance name
__in_z PCWSTR pInstanceName,
_In_z_ PCWSTR pInstanceName,
// reserved for the future use. Currently should be set to 0.
__in DWORD dwFlags
_In_ DWORD dwFlags
)
{
LOCALDB_PROXY(LocalDBDeleteInstance)(pInstanceName, dwFlags);
@ -1709,19 +1709,19 @@ LocalDBDeleteInstance (
HRESULT __cdecl
LocalDBFormatMessage(
// I the LocalDB error code
__in HRESULT hrLocalDB,
_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,
_In_ DWORD dwFlags,
// I Language desired (LCID) or 0 (in which case Win32 FormatMessage order is used)
__in DWORD dwLanguageId,
_In_ DWORD dwLanguageId,
// O the buffer to store the LocalDB error message
__out_ecount_z(*lpcchMessage) LPWSTR wszMessage,
_Out_writes_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
_Inout_ LPDWORD lpcchMessage
)
{
LOCALDB_PROXY(LocalDBFormatMessage)(hrLocalDB, dwFlags, dwLanguageId, wszMessage, lpcchMessage);
@ -1730,10 +1730,10 @@ LocalDBFormatMessage(
HRESULT __cdecl
LocalDBGetInstances(
// O buffer with instance names
__out PTLocalDBInstanceName pInstanceNames,
_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
_Inout_ LPDWORD lpdwNumberOfInstances
)
{
LOCALDB_PROXY(LocalDBGetInstances)(pInstanceNames, lpdwNumberOfInstances);
@ -1742,11 +1742,11 @@ LocalDBGetInstances(
HRESULT __cdecl
LocalDBGetInstanceInfo(
// I the instance name
__in_z PCWSTR wszInstanceName,
_In_z_ PCWSTR wszInstanceName,
// O instance information
__out PLocalDBInstanceInfo pInfo,
_Out_ PLocalDBInstanceInfo pInfo,
// I Size of LocalDBInstanceInfo structure in bytes
__in DWORD cbInfo
_In_ DWORD cbInfo
)
{
LOCALDB_PROXY(LocalDBGetInstanceInfo)(wszInstanceName, pInfo, cbInfo);
@ -1767,13 +1767,13 @@ LocalDBStopTracing()
HRESULT __cdecl
LocalDBShareInstance(
// I the SID of the LocalDB instance owner
__in_opt PSID pOwnerSID,
_In_opt_ PSID pOwnerSID,
// I the private name of LocalDB instance which should be shared
__in_z PCWSTR wszLocalDBInstancePrivateName,
_In_z_ PCWSTR wszLocalDBInstancePrivateName,
// I the public shared name
__in_z PCWSTR wszSharedName,
_In_z_ PCWSTR wszSharedName,
// I reserved for the future use. Currently should be set to 0.
__in DWORD dwFlags)
_In_ DWORD dwFlags)
{
LOCALDB_PROXY(LocalDBShareInstance)(pOwnerSID, wszLocalDBInstancePrivateName, wszSharedName, dwFlags);
}
@ -1781,10 +1781,10 @@ LocalDBShareInstance(
HRESULT __cdecl
LocalDBGetVersions(
// O buffer for installed LocalDB versions
__out PTLocalDBVersion pVersions,
_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
_Inout_ LPDWORD lpdwNumberOfVersions
)
{
LOCALDB_PROXY(LocalDBGetVersions)(pVersions, lpdwNumberOfVersions);
@ -1793,9 +1793,9 @@ LocalDBGetVersions(
HRESULT __cdecl
LocalDBUnshareInstance(
// I the LocalDB instance name
__in_z PCWSTR pInstanceName,
_In_z_ PCWSTR pInstanceName,
// I reserved for the future use. Currently should be set to 0.
__in DWORD dwFlags)
_In_ DWORD dwFlags)
{
LOCALDB_PROXY(LocalDBUnshareInstance)(pInstanceName, dwFlags);
}
@ -1803,11 +1803,11 @@ LocalDBUnshareInstance(
HRESULT __cdecl
LocalDBGetVersionInfo(
// I LocalDB version string
__in_z PCWSTR wszVersion,
_In_z_ PCWSTR wszVersion,
// O version information
__out PLocalDBVersionInfo pVersionInfo,
_Out_ PLocalDBVersionInfo pVersionInfo,
// I Size of LocalDBVersionInfo structure in bytes
__in DWORD cbVersionInfo)
_In_ DWORD cbVersionInfo)
{
LOCALDB_PROXY(LocalDBGetVersionInfo)(wszVersion, pVersionInfo, cbVersionInfo);
}

View file

@ -8,7 +8,7 @@
//
// Comments: Also contains "internal" declarations shared across source files.
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -73,7 +73,7 @@ typedef int socklen_t;
#pragma warning(pop)
#if PHP_MAJOR_VERSION > 5 || PHP_MAJOR_VERSION < 5 || ( PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION < 3 )
#if PHP_MAJOR_VERSION < 7
#error Trying to compile "Microsoft Drivers for PHP for SQL Server (SQLSRV Driver)" with an unsupported version of PHP
#endif
@ -148,7 +148,7 @@ struct ss_sqlsrv_conn : sqlsrv_conn
};
// resource destructor
void __cdecl sqlsrv_conn_dtor( zend_rsrc_list_entry *rsrc TSRMLS_DC );
void __cdecl sqlsrv_conn_dtor( zend_resource *rsrc TSRMLS_DC );
//*********************************************************************************************************************************
// Statement
@ -175,10 +175,10 @@ struct ss_sqlsrv_stmt : public sqlsrv_stmt {
void new_result_set( TSRMLS_D );
// driver specific conversion rules from a SQL Server/ODBC type to one of the SQLSRV_PHPTYPE_* constants
sqlsrv_phptype sql_type_to_php_type( SQLINTEGER sql_type, SQLUINTEGER size, bool prefer_string_to_stream );
sqlsrv_phptype sql_type_to_php_type( SQLINTEGER sql_type, SQLUINTEGER size, bool prefer_string_to_stream, bool prefer_number_to_string );
bool prepared; // whether the statement has been prepared yet (used for error messages)
int conn_index; // index into the connection hash that contains this statement structure
zend_ulong conn_index; // index into the connection hash that contains this statement structure
zval* params_z; // hold parameters passed to sqlsrv_prepare but not used until sqlsrv_execute
sqlsrv_fetch_field_name* fetch_field_names; // field names for current results used by sqlsrv_fetch_array/object as keys
int fetch_fields_count;
@ -223,7 +223,7 @@ PHP_FUNCTION(sqlsrv_rows_affected);
PHP_FUNCTION(sqlsrv_send_stream_data);
// resource destructor
void __cdecl sqlsrv_stmt_dtor( zend_rsrc_list_entry *rsrc TSRMLS_DC );
void __cdecl sqlsrv_stmt_dtor( zend_resource *rsrc TSRMLS_DC );
// "internal" statement functions shared by functions in conn.cpp and stmt.cpp
void bind_params( ss_sqlsrv_stmt* stmt TSRMLS_DC );
@ -265,15 +265,15 @@ 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)
long log_severity;
long log_subsystems;
long current_subsystem;
zend_long log_severity;
zend_long log_subsystems;
zend_long current_subsystem;
zend_bool warnings_return_as_errors;
long buffered_query_limit;
zend_long buffered_query_limit;
ZEND_END_MODULE_GLOBALS(sqlsrv)
@ -281,11 +281,11 @@ ZEND_EXTERN_MODULE_GLOBALS(sqlsrv);
}
// macros used to access the global variables. Use these to make global variable access agnostic to threads
#ifdef ZTS
#define SQLSRV_G(v) TSRMG(sqlsrv_globals_id, zend_sqlsrv_globals *, v)
#else
#define SQLSRV_G(v) sqlsrv_globals.v
// macro used to access the global variables. Use it to make global variable access agnostic to threads
#define SQLSRV_G(v) ZEND_MODULE_GLOBALS_ACCESSOR(sqlsrv, v)
#if defined(ZTS)
ZEND_TSRMLS_CACHE_EXTERN();
#endif
// INI settings and constants
@ -353,6 +353,7 @@ enum SS_ERROR_CODES {
SS_SQLSRV_ERROR_CONNECT_ILLEGAL_ENCODING,
SS_SQLSRV_ERROR_CONNECT_BRACES_NOT_ESCAPED,
SS_SQLSRV_ERROR_INVALID_OUTPUT_PARAM_TYPE,
SS_SQLSRV_ERROR_PARAM_VAR_NOT_REF
};
extern ss_error SS_ERRORS[];
@ -367,42 +368,42 @@ PHP_FUNCTION(sqlsrv_errors);
// bytes. The return is the number of UTF-16 characters in the string
// returned in utf16_out_string.
unsigned int convert_string_from_default_encoding( unsigned int php_encoding, char const* mbcs_in_string,
unsigned int mbcs_len, __out wchar_t* utf16_out_string,
unsigned int mbcs_len, _Out_ wchar_t* utf16_out_string,
unsigned int utf16_len );
// create a wide char string from the passed in mbcs string. NULL is returned if the string
// could not be created. No error is posted by this function. utf16_len is the number of
// wchar_t characters, not the number of bytes.
wchar_t* utf16_string_from_mbcs_string( unsigned int php_encoding, const char* mbcs_string,
unsigned int mbcs_len, __out unsigned int* utf16_len );
unsigned int mbcs_len, _Out_ unsigned int* utf16_len );
// *** internal error macros and functions ***
bool handle_error( sqlsrv_context const* ctx, int log_subsystem, const char* function,
sqlsrv_error const* ssphp TSRMLS_DC, ... );
void handle_warning( sqlsrv_context const* ctx, int log_subsystem, const char* function,
sqlsrv_error const* ssphp TSRMLS_DC, ... );
void __cdecl sqlsrv_error_dtor( zend_rsrc_list_entry *rsrc TSRMLS_DC );
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, ... ) \
@ -455,8 +456,7 @@ public:
#define LOG_FUNCTION( function_name ) \
const char* _FN_ = function_name; \
SQLSRV_G( current_subsystem ) = current_log_subsystem; \
LOG( SEV_NOTICE, "%1!s!: entering", _FN_ ); \
CheckMemory _check_memory_;
LOG( SEV_NOTICE, "%1!s!: entering", _FN_ );
#define SET_FUNCTION_NAME( context ) \
{ \
@ -475,21 +475,6 @@ enum logging_subsystems {
LOG_ALL = -1,
};
struct CheckMemory {
CheckMemory( void )
{
// test the integrity of the Zend heap.
full_mem_check(MEMCHECK_SILENT);
}
~CheckMemory( void )
{
// test the integrity of the Zend heap.
full_mem_check(MEMCHECK_SILENT);
}
};
//*********************************************************************************************************************************
// Utility Functions
@ -498,11 +483,8 @@ struct CheckMemory {
// generic function used to validate parameters to a PHP function.
// Register an invalid parameter error and returns NULL when parameters don't match the spec given.
template <typename H>
inline H* process_params( INTERNAL_FUNCTION_PARAMETERS, char const* param_spec, const char* calling_func, int param_count, ... )
inline H* process_params( INTERNAL_FUNCTION_PARAMETERS, char const* param_spec, const char* calling_func, size_t param_count, ... )
{
SQLSRV_UNUSED( return_value_used );
SQLSRV_UNUSED( this_ptr );
SQLSRV_UNUSED( return_value_ptr );
SQLSRV_UNUSED( return_value );
zval* rsrc;
@ -527,7 +509,7 @@ inline H* process_params( INTERNAL_FUNCTION_PARAMETERS, char const* param_spec,
va_list vaList;
va_start(vaList, param_count); //set the pointer to first argument
for(int i=0; i< param_count; ++i) {
for(size_t i = 0; i < param_count; ++i) {
arr[i] = va_arg(vaList, void*);
}
@ -588,7 +570,7 @@ inline H* process_params( INTERNAL_FUNCTION_PARAMETERS, char const* param_spec,
}
// get the resource registered
h = static_cast<H*>( zend_fetch_resource( &rsrc TSRMLS_CC, -1, H::resource_name, NULL, 1, H::descriptor ));
h = static_cast<H*>( zend_fetch_resource(Z_RES_P(rsrc) TSRMLS_CC, H::resource_name, H::descriptor ));
CHECK_CUSTOM_ERROR(( h == NULL ), &error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, calling_func ) {
@ -622,13 +604,14 @@ namespace ss {
}
};
inline void zend_register_resource( __out zval* rsrc_result, void* rsrc_pointer, int rsrc_type, char* rsrc_name TSRMLS_DC )
inline void zend_register_resource(_Out_ zval& rsrc_result, void* rsrc_pointer, int rsrc_type, char* rsrc_name TSRMLS_DC)
{
int zr = ZEND_REGISTER_RESOURCE( rsrc_result, rsrc_pointer, rsrc_type );
int zr = (NULL != (Z_RES(rsrc_result) = ::zend_register_resource(rsrc_pointer, rsrc_type)) ? SUCCESS : FAILURE);
CHECK_CUSTOM_ERROR(( zr == FAILURE ), reinterpret_cast<sqlsrv_context*>( rsrc_pointer ), SS_SQLSRV_ERROR_REGISTER_RESOURCE,
rsrc_name ) {
rsrc_name ) {
throw ss::SSException();
}
Z_TYPE_INFO(rsrc_result) = IS_RESOURCE_EX;
}
} // namespace ss

View file

@ -3,7 +3,7 @@
//
// Contents: Routines that use statement handles
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -53,15 +53,18 @@ const char STDCLASS_NAME_LEN = sizeof( STDCLASS_NAME ) - 1;
// map a Zend PHP type constant to our constant type
enum SQLSRV_PHPTYPE zend_to_sqlsrv_phptype[] = {
SQLSRV_PHPTYPE_INVALID,
SQLSRV_PHPTYPE_NULL,
SQLSRV_PHPTYPE_INVALID,
SQLSRV_PHPTYPE_INVALID,
SQLSRV_PHPTYPE_INT,
SQLSRV_PHPTYPE_FLOAT,
SQLSRV_PHPTYPE_INVALID,
SQLSRV_PHPTYPE_STRING,
SQLSRV_PHPTYPE_INVALID,
SQLSRV_PHPTYPE_DATETIME,
SQLSRV_PHPTYPE_STRING,
SQLSRV_PHPTYPE_STREAM,
SQLSRV_PHPTYPE_INVALID,
SQLSRV_PHPTYPE_INVALID,
SQLSRV_PHPTYPE_INVALID
};
@ -85,22 +88,23 @@ 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, int fetch_type, __out zval* return_value, bool allow_empty_field_names
TSRMLS_DC );
bool determine_column_size_or_precision( sqlsrv_stmt const* stmt, sqlsrv_sqltype sqlsrv_type, __out SQLUINTEGER* column_size,
__out SQLSMALLINT* decimal_digits );
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 );
void determine_stmt_has_rows( ss_sqlsrv_stmt* stmt TSRMLS_DC );
bool is_valid_sqlsrv_phptype( sqlsrv_phptype type );
bool is_valid_sqlsrv_sqltype( sqlsrv_sqltype type );
void parse_param_array( ss_sqlsrv_stmt* stmt, __inout zval* param_array, unsigned long index, __out int& direction,
__out SQLSRV_PHPTYPE& php_out_type, __out SQLSRV_ENCODING& encoding, __out SQLSMALLINT& sql_type,
__out SQLULEN& column_size, __out SQLSMALLINT& decimal_digits TSRMLS_DC );
void parse_param_array( ss_sqlsrv_stmt* stmt, _Inout_ zval* param_array, zend_ulong index, _Out_ SQLSMALLINT& direction,
_Out_ SQLSRV_PHPTYPE& php_out_type, _Out_ SQLSRV_ENCODING& encoding, _Out_ SQLSMALLINT& sql_type,
_Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits TSRMLS_DC );
void type_and_encoding( INTERNAL_FUNCTION_PARAMETERS, int type );
void type_and_size_calc( INTERNAL_FUNCTION_PARAMETERS, int type );
void type_and_precision_calc( INTERNAL_FUNCTION_PARAMETERS, int type );
bool verify_and_set_encoding( const char* encoding_string, __out sqlsrv_phptype& phptype_encoding TSRMLS_DC );
bool verify_and_set_encoding( const char* encoding_string, _Out_ sqlsrv_phptype& phptype_encoding TSRMLS_DC );
}
@ -135,10 +139,9 @@ ss_sqlsrv_stmt::~ss_sqlsrv_stmt( void )
}
sqlsrv_free( fetch_field_names );
}
if( params_z ) {
zval_ptr_dtor( &params_z );
params_z = NULL;
zval_ptr_dtor( params_z );
sqlsrv_free(params_z);
}
}
@ -161,7 +164,7 @@ void ss_sqlsrv_stmt::new_result_set( TSRMLS_D )
}
// Returns a php type for a given sql type. Also sets the encoding wherever applicable.
sqlsrv_phptype ss_sqlsrv_stmt::sql_type_to_php_type( SQLINTEGER sql_type, SQLUINTEGER size, bool prefer_string_to_stream )
sqlsrv_phptype ss_sqlsrv_stmt::sql_type_to_php_type( SQLINTEGER sql_type, SQLUINTEGER size, bool prefer_string_to_stream, bool prefer_number_to_string )
{
sqlsrv_phptype ss_phptype;
ss_phptype.typeinfo.type = SQLSRV_PHPTYPE_INVALID;
@ -292,7 +295,7 @@ PHP_FUNCTION( sqlsrv_execute )
bind_params( stmt TSRMLS_CC );
core_sqlsrv_execute( stmt TSRMLS_CC );
RETURN_TRUE;
}
catch( core::CoreException& ) {
@ -326,8 +329,9 @@ PHP_FUNCTION( sqlsrv_fetch )
LOG_FUNCTION( "sqlsrv_fetch" );
ss_sqlsrv_stmt* stmt = NULL;
// NOTE: zend_parse_parameter expect zend_long when the type spec is 'l',and core_sqlsrv_fetch expect short int
int fetch_style = SQL_FETCH_NEXT; // default value for parameter if one isn't supplied
int fetch_offset = 0; // default value for parameter if one isn't supplied
zend_long fetch_offset = 0; // default value for parameter if one isn't supplied
// take only the statement resource
PROCESS_PARAMS( stmt, "r|ll", _FN_, 2, &fetch_style, &fetch_offset );
@ -378,9 +382,9 @@ PHP_FUNCTION( sqlsrv_fetch_array )
LOG_FUNCTION( "sqlsrv_fetch_array" );
ss_sqlsrv_stmt* stmt = NULL;
int fetch_type = SQLSRV_FETCH_BOTH; // default value for parameter if one isn't supplied
int fetch_style = SQL_FETCH_NEXT; // default value for parameter if one isn't supplied
int fetch_offset = 0; // default value for parameter if one isn't supplied
zend_long fetch_type = SQLSRV_FETCH_BOTH; // default value for parameter if one isn't supplied
int fetch_style = SQL_FETCH_NEXT; // default value for parameter if one isn't supplied
zend_long fetch_offset = 0; // default value for parameter if one isn't supplied
// retrieve the statement resource and optional fetch type (see enum SQLSRV_FETCH_TYPE),
// fetch style (see SQLSRV_SCROLL_* constants) and fetch offset
@ -402,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& ) {
@ -450,9 +456,9 @@ PHP_FUNCTION( sqlsrv_field_metadata )
// get the number of fields in the resultset
num_cols = core::SQLNumResultCols( stmt TSRMLS_CC );
zval_auto_ptr result_meta_data;
ALLOC_INIT_ZVAL( result_meta_data );
core::sqlsrv_array_init( *stmt, result_meta_data TSRMLS_CC );
zval result_meta_data;
ZVAL_UNDEF( &result_meta_data );
core::sqlsrv_array_init( *stmt, &result_meta_data TSRMLS_CC );
for( SQLSMALLINT f = 0; f < num_cols; ++f ) {
@ -460,16 +466,16 @@ PHP_FUNCTION( sqlsrv_field_metadata )
core_meta_data = core_sqlsrv_field_metadata( stmt, f TSRMLS_CC );
// initialize the array
zval_auto_ptr field_array;
ALLOC_INIT_ZVAL( field_array );
core::sqlsrv_array_init( *stmt, field_array TSRMLS_CC );
zval field_array;
ZVAL_UNDEF( &field_array );
core::sqlsrv_array_init( *stmt, &field_array TSRMLS_CC );
core::sqlsrv_add_assoc_string( *stmt, field_array, FieldMetaData::NAME,
core::sqlsrv_add_assoc_string( *stmt, &field_array, FieldMetaData::NAME,
reinterpret_cast<char*>( core_meta_data->field_name.get() ), 0 TSRMLS_CC );
core_meta_data->field_name.transferred();
core::sqlsrv_add_assoc_long( *stmt, field_array, FieldMetaData::TYPE, core_meta_data->field_type TSRMLS_CC );
core::sqlsrv_add_assoc_long( *stmt, &field_array, FieldMetaData::TYPE, core_meta_data->field_type TSRMLS_CC );
switch( core_meta_data->field_type ) {
case SQL_DECIMAL:
@ -478,9 +484,9 @@ PHP_FUNCTION( sqlsrv_field_metadata )
case SQL_TYPE_DATE:
case SQL_SS_TIME2:
case SQL_SS_TIMESTAMPOFFSET:
core::sqlsrv_add_assoc_null( *stmt, field_array, FieldMetaData::SIZE TSRMLS_CC );
core::sqlsrv_add_assoc_long( *stmt, field_array, FieldMetaData::PREC, core_meta_data->field_precision TSRMLS_CC );
core::sqlsrv_add_assoc_long( *stmt, field_array, FieldMetaData::SCALE, core_meta_data->field_scale TSRMLS_CC );
core::sqlsrv_add_assoc_null( *stmt, &field_array, FieldMetaData::SIZE TSRMLS_CC );
core::sqlsrv_add_assoc_long( *stmt, &field_array, FieldMetaData::PREC, core_meta_data->field_precision TSRMLS_CC );
core::sqlsrv_add_assoc_long( *stmt, &field_array, FieldMetaData::SCALE, core_meta_data->field_scale TSRMLS_CC );
break;
case SQL_BIT:
case SQL_TINYINT:
@ -490,33 +496,30 @@ PHP_FUNCTION( sqlsrv_field_metadata )
case SQL_REAL:
case SQL_FLOAT:
case SQL_DOUBLE:
core::sqlsrv_add_assoc_null( *stmt, field_array, FieldMetaData::SIZE TSRMLS_CC );
core::sqlsrv_add_assoc_long( *stmt, field_array, FieldMetaData::PREC, core_meta_data->field_precision TSRMLS_CC );
core::sqlsrv_add_assoc_null( *stmt, field_array, FieldMetaData::SCALE TSRMLS_CC );
core::sqlsrv_add_assoc_null( *stmt, &field_array, FieldMetaData::SIZE TSRMLS_CC );
core::sqlsrv_add_assoc_long( *stmt, &field_array, FieldMetaData::PREC, core_meta_data->field_precision TSRMLS_CC );
core::sqlsrv_add_assoc_null( *stmt, &field_array, FieldMetaData::SCALE TSRMLS_CC );
break;
default:
core::sqlsrv_add_assoc_long( *stmt, field_array, FieldMetaData::SIZE, core_meta_data->field_size TSRMLS_CC );
core::sqlsrv_add_assoc_null( *stmt, field_array, FieldMetaData::PREC TSRMLS_CC );
core::sqlsrv_add_assoc_null( *stmt, field_array, FieldMetaData::SCALE TSRMLS_CC );
core::sqlsrv_add_assoc_long( *stmt, &field_array, FieldMetaData::SIZE, core_meta_data->field_size TSRMLS_CC );
core::sqlsrv_add_assoc_null( *stmt, &field_array, FieldMetaData::PREC TSRMLS_CC );
core::sqlsrv_add_assoc_null( *stmt, &field_array, FieldMetaData::SCALE TSRMLS_CC );
break;
}
// add the nullability to the array
core::sqlsrv_add_assoc_long( *stmt, field_array, FieldMetaData::NULLABLE, core_meta_data->field_is_nullable
core::sqlsrv_add_assoc_long( *stmt, &field_array, FieldMetaData::NULLABLE, core_meta_data->field_is_nullable
TSRMLS_CC );
// add this field's meta data to the result set meta data
core::sqlsrv_add_next_index_zval( *stmt, result_meta_data, field_array TSRMLS_CC );
field_array.transferred();
core::sqlsrv_add_next_index_zval( *stmt, &result_meta_data, &field_array TSRMLS_CC );
// always good to call destructor for allocations done through placement new operator.
core_meta_data->~field_meta_data();
}
// return our built collection and transfer ownership
zval_ptr_dtor( &return_value );
*return_value_ptr = result_meta_data;
result_meta_data.transferred();
RETURN_ZVAL(&result_meta_data, 1, 1);
}
catch( core::CoreException& ) {
@ -596,7 +599,7 @@ PHP_FUNCTION( sqlsrv_rows_affected )
LOG_FUNCTION( "sqlsrv_rows_affected" );
SQLRETURN r = SQL_SUCCESS;
ss_sqlsrv_stmt* stmt = NULL;
SQLINTEGER rows = -1;
SQLLEN rows = -1;
PROCESS_PARAMS( stmt, "r", _FN_, 0 );
@ -644,7 +647,7 @@ PHP_FUNCTION( sqlsrv_num_rows )
LOG_FUNCTION( "sqlsrv_num_rows" );
ss_sqlsrv_stmt* stmt = NULL;
SQLINTEGER rows = -1;
SQLLEN rows = -1;
PROCESS_PARAMS( stmt, "r", _FN_, 0 );
@ -759,15 +762,17 @@ PHP_FUNCTION( sqlsrv_fetch_object )
LOG_FUNCTION( "sqlsrv_fetch_object" );
ss_sqlsrv_stmt* stmt = NULL;
zval* class_name_z = NULL;
zval* ctor_params_z = NULL;
zval* class_name_z = NULL;
zval* ctor_params_z = NULL;
int fetch_style = SQL_FETCH_NEXT; // default value for parameter if one isn't supplied
int fetch_offset = 0; // default value for parameter if one isn't supplied
zend_long fetch_offset = 0; // default value for parameter if one isn't supplied
// stdClass is the name of the system's default base class in PHP
char* class_name = const_cast<char*>( STDCLASS_NAME );
unsigned int class_name_len = STDCLASS_NAME_LEN;
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
@ -787,8 +792,8 @@ PHP_FUNCTION( sqlsrv_fetch_object )
CHECK_CUSTOM_ERROR(( Z_TYPE_P( class_name_z ) != IS_STRING ), stmt, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ) {
throw ss::SSException();
}
class_name = Z_STRVAL_P( class_name_z );
class_name_len = Z_STRLEN_P( class_name_z );
class_name = Z_STRVAL( *class_name_z );
class_name_len = Z_STRLEN( *class_name_z );
zend_str_tolower( class_name, class_name_len );
}
@ -802,19 +807,21 @@ 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;
int zr = zend_lookup_class( class_name, class_name_len, &class_entry TSRMLS_CC );
zend_class_entry* class_entry = NULL;
zend_string* class_name_str_z = zend_string_init( class_name, class_name_len, 0 );
int zr = ( NULL != ( class_entry = zend_lookup_class( class_name_str_z TSRMLS_CC ))) ? SUCCESS : FAILURE;
zend_string_release( class_name_str_z );
CHECK_ZEND_ERROR( zr, stmt, SS_SQLSRV_ERROR_ZEND_BAD_CLASS, class_name ) {
throw ss::SSException();
}
// 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();
}
@ -824,7 +831,9 @@ 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, 1 /*destroy_ht*/ TSRMLS_CC );
zend_merge_properties( &retval_z, properties_ht TSRMLS_CC );
zend_hash_destroy( properties_ht );
FREE_HASHTABLE( properties_ht );
// find and call the object's constructor
@ -839,30 +848,30 @@ PHP_FUNCTION( sqlsrv_fetch_object )
// calling zend_fcall_info_init with a test callable.
// if there is a constructor (e.g., stdClass doesn't have one)
if( (*class_entry)->constructor ) {
if( class_entry->constructor ) {
// take the parameters given as our last argument and put them into a sequential array
sqlsrv_malloc_auto_ptr<zval**> params_m;
zval_auto_ptr ctor_retval_z;
ALLOC_INIT_ZVAL( ctor_retval_z );
sqlsrv_malloc_auto_ptr<zval> params_m;
zval ctor_retval_z;
ZVAL_UNDEF( &ctor_retval_z );
int num_params = 0;
if( ctor_params_z != NULL ) {
HashTable* ctor_params_ht = Z_ARRVAL_P( ctor_params_z );
if ( ctor_params_z ) {
HashTable* ctor_params_ht = Z_ARRVAL( *ctor_params_z );
num_params = zend_hash_num_elements( ctor_params_ht );
params_m = reinterpret_cast<zval***>( sqlsrv_malloc( num_params * sizeof( zval**) ));
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 ) {
zr = zend_hash_get_current_data_ex( ctor_params_ht, reinterpret_cast<void**>(&(params_m[ i ])), NULL );
CHECK_ZEND_ERROR( zr, stmt, SS_SQLSRV_ERROR_ZEND_OBJECT_FAILED, class_name ) {
throw ss::SSException();
}
} //for
} //if( ctor_params_z != NULL )
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();
}
i++;
} ZEND_HASH_FOREACH_END();
} //if( !Z_ISUNDEF( ctor_params_z ))
// call the constructor function itself.
zend_fcall_info fci;
@ -870,32 +879,28 @@ PHP_FUNCTION( sqlsrv_fetch_object )
memset( &fci, 0, sizeof( fci ));
fci.size = sizeof( fci );
fci.function_table = &(*class_entry)->function_table;
fci.function_name = NULL;
fci.retval_ptr_ptr = &ctor_retval_z;
fci.function_table = &( class_entry )->function_table;
ZVAL_UNDEF( &( fci.function_name ) );
fci.retval = &ctor_retval_z;
fci.param_count = num_params;
fci.params = params_m; // purposefully not transferred since ownership isn't actually transferred.
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION <= 2
fci.object_pp = &return_value;
#else
fci.object_ptr = return_value;
#endif
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;
#if PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION <= 2
fcic.object_pp = &return_value;
#else
fcic.object_ptr = return_value;
#endif
fcic.function_handler = class_entry->constructor;
fcic.calling_scope = class_entry;
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 ) {
throw ss::SSException();
}
} //if( (*class_entry)->constructor )
} //if( class_entry->constructor )
RETURN_ZVAL( &retval_z, 1, 1 );
}
catch( core::CoreException& ) {
@ -905,6 +910,10 @@ PHP_FUNCTION( sqlsrv_fetch_object )
zend_hash_destroy( properties_ht );
FREE_HASHTABLE( properties_ht );
}
else if ( Z_TYPE( retval_z ) == IS_ARRAY ) {
zend_hash_destroy( Z_ARRVAL( retval_z ));
FREE_HASHTABLE( Z_ARRVAL( retval_z ));
}
RETURN_FALSE;
}
@ -1059,6 +1068,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 );
@ -1066,15 +1077,17 @@ PHP_FUNCTION( sqlsrv_get_field )
try {
// validate that the field index is within range
SQLSMALLINT num_cols = core::SQLNumResultCols( stmt TSRMLS_CC );
int num_cols = core::SQLNumResultCols( stmt TSRMLS_CC );
if( field_index < 0 || field_index >= num_cols ) {
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_ptr_dtor( &return_value );
*return_value_ptr = convert_to_zval( sqlsrv_php_type_out, &field_value, field_len );
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& ) {
@ -1167,25 +1180,20 @@ 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 )) {
char *key = NULL;
unsigned int key_len = 0;
unsigned long index = -1;
zval** value_z = NULL;
// make sure it's an integer index
int type = zend_hash_get_current_key_ex( params_ht, &key, &key_len, &index, 0, NULL );
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, (void**) &value_z TSRMLS_CC );
zend_ulong index;
zend_string* key = NULL;
zval* value_z = NULL;
ZEND_HASH_FOREACH_KEY_VAL( params_ht, index, key, value_z ) {
// 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();
}
// 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
@ -1193,32 +1201,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_PP( value_z ) != IS_ARRAY ) {
// print a warning if they didn't send it as a reference
if( !PZVAL_IS_REF( *value_z ) && Z_REFCOUNT_P( *value_z ) > 1 ) {
php_error( E_WARNING, SS_SQLSRV_WARNING_PARAM_VAR_NOT_REF, index + 1 );
}
Z_SET_ISREF_PP( value_z ); // mark it as a reference
}
// else mark [0] as a reference
else {
zval** var = NULL;
int zr = zend_hash_index_find( Z_ARRVAL_PP( value_z ), 0, reinterpret_cast<void**>( &var ));
CHECK_CUSTOM_ERROR( zr == FAILURE, stmt, SS_SQLSRV_ERROR_VAR_REQUIRED, index + 1 ) {
throw ss::SSException();
}
// print a warning if they didn't send it as a reference
if( !PZVAL_IS_REF( *var ) && Z_REFCOUNT_P( *var ) > 1 ) {
php_error( E_WARNING, SS_SQLSRV_WARNING_PARAM_VAR_NOT_REF, index + 1 );
}
Z_SET_ISREF_PP( var ); // mark it as a reference
}
}
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 ) {
throw ss::SSException();
}
ZVAL_MAKE_REF( var );
}
} ZEND_HASH_FOREACH_END();
// save our parameters for later.
zval_add_ref( &params_z );
Z_TRY_ADDREF_P( params_z );
stmt->params_z = params_z;
}
@ -1236,66 +1233,58 @@ void bind_params( ss_sqlsrv_stmt* stmt TSRMLS_DC )
stmt->executed = false;
zval* params_z = stmt->params_z;
HashTable* params_ht = Z_ARRVAL_P( params_z );
zend_ulong index = -1;
zend_string *key = NULL;
zval* param_z = NULL;
for( zend_hash_internal_pointer_reset( params_ht );
zend_hash_has_more_elements( params_ht ) == SUCCESS;
zend_hash_move_forward( params_ht )) {
char *key = NULL;
unsigned int key_len = 0;
unsigned long index = -1;
zval** param_z = NULL;
zval* value_z = NULL;
int 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_ex( params_ht, &key, &key_len, &index, 0, NULL );
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, (void**) &param_z TSRMLS_CC );
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 = 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_PP( param_z ) == IS_ARRAY ) {
if( Z_TYPE_P( param_z ) == IS_ARRAY ) {
zval** var = NULL;
int zr = zend_hash_index_find( Z_ARRVAL_PP( param_z ), 0, reinterpret_cast<void**>( &var ));
zval* var = NULL;
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 ) {
zval_ptr_dtor( &params_z );
throw ss::SSException();
}
// parse the parameter array that the user gave
parse_param_array( stmt, *param_z, index, direction, php_out_type, encoding, sql_type, column_size,
decimal_digits TSRMLS_CC );
value_z = *var;
parse_param_array( stmt, param_z, index, direction, php_out_type, encoding, sql_type, column_size,
decimal_digits TSRMLS_CC );
value_z = var;
}
else {
value_z = *param_z;
value_z = param_z;
}
// bind the parameter
core_sqlsrv_bind_param( stmt, index, direction, value_z, php_out_type, encoding, sql_type, column_size, decimal_digits
TSRMLS_CC );
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 );
zval_ptr_dtor( &stmt->params_z );
zval_ptr_dtor( stmt->params_z );
sqlsrv_free( stmt->params_z );
stmt->params_z = NULL;
throw;
}
@ -1344,13 +1333,12 @@ PHP_FUNCTION( sqlsrv_cancel )
}
}
void __cdecl sqlsrv_stmt_dtor( zend_rsrc_list_entry *rsrc TSRMLS_DC )
void __cdecl sqlsrv_stmt_dtor(zend_resource *rsrc TSRMLS_DC)
{
LOG_FUNCTION( "sqlsrv_stmt_dtor" );
// get the structure
ss_sqlsrv_stmt *stmt = static_cast<ss_sqlsrv_stmt*>( rsrc->ptr );
if( stmt->conn ) {
int zr = zend_hash_index_del( static_cast<ss_sqlsrv_conn*>( stmt->conn )->stmts, stmt->conn_index );
if( zr == FAILURE ) {
@ -1385,9 +1373,6 @@ void __cdecl sqlsrv_stmt_dtor( zend_rsrc_list_entry *rsrc TSRMLS_DC )
PHP_FUNCTION( sqlsrv_free_stmt )
{
SQLSRV_UNUSED( return_value_used );
SQLSRV_UNUSED( this_ptr );
SQLSRV_UNUSED( return_value_ptr );
LOG_FUNCTION( "sqlsrv_free_stmt" );
@ -1395,8 +1380,6 @@ PHP_FUNCTION( sqlsrv_free_stmt )
ss_sqlsrv_stmt* stmt = NULL;
sqlsrv_context_auto_ptr error_ctx;
// we do this manually instead of with PROCESS_PARAMS because we return TRUE even if there is a parameter error.
full_mem_check(MEMCHECK_SILENT);
reset_errors( TSRMLS_C );
try {
@ -1426,22 +1409,26 @@ PHP_FUNCTION( sqlsrv_free_stmt )
}
// verify the resource so we know we're deleting a statement
stmt = static_cast<ss_sqlsrv_stmt*>( zend_fetch_resource( &stmt_r TSRMLS_CC, -1, ss_sqlsrv_stmt::resource_name, NULL,
1, ss_sqlsrv_stmt::descriptor ));
stmt = static_cast<ss_sqlsrv_stmt*>(zend_fetch_resource_ex(stmt_r TSRMLS_CC, ss_sqlsrv_stmt::resource_name, ss_sqlsrv_stmt::descriptor));
// if sqlsrv_free_stmt was called on an already closed statment then we just return success.
// zend_list_close sets the type of the closed statment to -1.
if ( Z_RES_TYPE_P( stmt_r ) == RSRC_INVALID_TYPE ) {
RETURN_TRUE;
}
if( stmt == NULL ) {
THROW_CORE_ERROR( error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ );
}
// delete the resource from Zend's master list, which will trigger the statement's destructor
int zr = zend_hash_index_del( &EG( regular_list ), Z_RESVAL_P( stmt_r ));
if( zr == FAILURE ) {
LOG( SEV_ERROR, "Failed to remove stmt resource %1!d!", Z_RESVAL_P( stmt_r ));
if( zend_list_close( Z_RES_P(stmt_r) ) == FAILURE ) {
LOG( SEV_ERROR, "Failed to remove stmt resource %1!d!", Z_RES_P( stmt_r )->handle);
}
ZVAL_NULL( stmt_r );
RETURN_TRUE;
}
@ -1463,7 +1450,7 @@ void stmt_option_scrollable:: operator()( sqlsrv_stmt* stmt, stmt_option const*
}
const char* scroll_type = Z_STRVAL_P( value_z );
unsigned int cursor_type = -1;
unsigned long cursor_type = -1;
// find which cursor type they would like and set the ODBC statement attribute as such
if( !stricmp( scroll_type, SSCursorTypes::QUERY_OPTION_SCROLLABLE_STATIC )) {
@ -1501,75 +1488,64 @@ 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 ) {
ZVAL_NULL( &out_zval);
return;
}
switch( sqlsrv_php_type ) {
case SQLSRV_PHPTYPE_INT:
case SQLSRV_PHPTYPE_FLOAT:
{
ALLOC_INIT_ZVAL( out_zval );
if( *in_val == NULL ) {
ZVAL_NULL( out_zval );
}
else {
switch (sqlsrv_php_type) {
if( sqlsrv_php_type == SQLSRV_PHPTYPE_INT ) {
ZVAL_LONG( out_zval, **( reinterpret_cast<long**>( in_val )));
}
else {
ZVAL_DOUBLE( out_zval, **( reinterpret_cast<double**>( in_val )));
}
}
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;
}
if( *in_val ) {
sqlsrv_free( *in_val );
}
case SQLSRV_PHPTYPE_STRING:
{
ZVAL_STRINGL( &out_zval, static_cast<const char*>( in_val ), field_len);
break;
}
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;
}
case SQLSRV_PHPTYPE_STRING:
{
ALLOC_INIT_ZVAL( out_zval );
case SQLSRV_PHPTYPE_NULL:
ZVAL_NULL(&out_zval);
break;
if( *in_val == NULL ) {
ZVAL_NULL( out_zval );
}
else {
ZVAL_STRINGL( out_zval, reinterpret_cast<char*>( *in_val ), field_len, 0 /*duplicate*/ );
}
break;
}
case SQLSRV_PHPTYPE_STREAM:
case SQLSRV_PHPTYPE_DATETIME:
out_zval = ( reinterpret_cast<zval*>( *in_val ));
break;
case SQLSRV_PHPTYPE_NULL:
ALLOC_INIT_ZVAL( out_zval );
ZVAL_NULL( out_zval );
break;
default:
DIE( "Unknown php type" );
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 SQLUINTEGER* column_size,
__out SQLSMALLINT* decimal_digits )
bool determine_column_size_or_precision( sqlsrv_stmt const* stmt, sqlsrv_sqltype sqlsrv_type, _Out_ SQLULEN* column_size,
_Out_ SQLSMALLINT* decimal_digits )
{
*decimal_digits = 0;
@ -1817,105 +1793,101 @@ void determine_stmt_has_rows( ss_sqlsrv_stmt* stmt TSRMLS_DC )
}
}
void fetch_fields_common( __inout ss_sqlsrv_stmt* stmt, int 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_auto_ptr 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_s(( void* )field_names[i].name, ( field_name_len * sizeof( char )) ,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;
}
MAKE_STD_ZVAL( fields );
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 ) {
// this zval_auto_ptr is never transferred because we rely on its destructor to decrement the reference count
// we increment its reference count within each fetch type (below)
zval_auto_ptr field;
SQLLEN field_len = -1;
int zr = array_init( &fields );
CHECK_ZEND_ERROR( zr, stmt, SQLSRV_ERROR_ZEND_HASH ) {
throw ss::SSException();
}
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();
}
zval_add_ref( &field );
}
for( int i = 0; i < num_cols; ++i ) {
SQLLEN field_len = -1;
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();
}
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 );
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();
}
zval_add_ref( &field );
}
}
} //for loop
zval field;
ZVAL_UNDEF( &field );
convert_to_zval( stmt, sqlsrv_php_type_out, field_value, field_len, field );
sqlsrv_free( field_value );
if( fetch_type & SQLSRV_FETCH_NUMERIC ) {
zr = add_next_index_zval( &fields, &field );
CHECK_ZEND_ERROR( zr, stmt, SQLSRV_ERROR_ZEND_HASH ) {
throw ss::SSException();
}
}
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 NUMERIC 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 );
fields.transferred();
}
void parse_param_array( ss_sqlsrv_stmt* stmt, __inout zval* param_array, unsigned long index, __out int& direction,
__out SQLSRV_PHPTYPE& php_out_type, __out SQLSRV_ENCODING& encoding, __out SQLSMALLINT& sql_type,
__out SQLULEN& column_size, __out SQLSMALLINT& decimal_digits TSRMLS_DC )
void parse_param_array( ss_sqlsrv_stmt* stmt, _Inout_ zval* param_array, zend_ulong index, _Out_ SQLSMALLINT& direction,
_Out_ SQLSRV_PHPTYPE& php_out_type, _Out_ SQLSRV_ENCODING& encoding, _Out_ SQLSMALLINT& sql_type,
_Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits TSRMLS_DC )
{
zval** var_or_val;
zval** temp;
zval* var_or_val = NULL;
zval* temp = NULL;
HashTable* param_ht = Z_ARRVAL_P( param_array );
sqlsrv_sqltype sqlsrv_sql_type;
@ -1930,42 +1902,47 @@ void parse_param_array( ss_sqlsrv_stmt* stmt, __inout zval* param_array, unsigne
// handle the array parameters that contain the value/var, direction, php_type, sql_type
zend_hash_internal_pointer_reset( param_ht );
if( zend_hash_has_more_elements( param_ht ) == FAILURE ||
zend_hash_get_current_data( param_ht, (void**) &var_or_val ) == FAILURE ) {
(var_or_val = zend_hash_get_current_data(param_ht)) == NULL) {
THROW_SS_ERROR( stmt, SS_SQLSRV_ERROR_VAR_REQUIRED, index + 1 );
}
// if the direction is included, then use what they gave, otherwise INPUT is assumed
if( zend_hash_move_forward( param_ht ) == SUCCESS && zend_hash_get_current_data( param_ht, (void**) &temp ) == SUCCESS &&
Z_TYPE_PP( temp ) != IS_NULL ) {
if (zend_hash_move_forward(param_ht) == SUCCESS && (temp = zend_hash_get_current_data(param_ht)) != NULL &&
Z_TYPE_P( temp ) != IS_NULL ) {
CHECK_CUSTOM_ERROR( Z_TYPE_PP( temp ) != IS_LONG, stmt, SS_SQLSRV_ERROR_INVALID_PARAMETER_DIRECTION, index + 1 ) {
CHECK_CUSTOM_ERROR( Z_TYPE_P( temp ) != IS_LONG, stmt, SS_SQLSRV_ERROR_INVALID_PARAMETER_DIRECTION, index + 1 ) {
throw ss::SSException();
}
direction = Z_LVAL_PP( temp );
direction = static_cast<SQLSMALLINT>(Z_LVAL_P( temp ));
CHECK_CUSTOM_ERROR( direction != SQL_PARAM_INPUT && direction != SQL_PARAM_OUTPUT && direction != SQL_PARAM_INPUT_OUTPUT,
stmt, SS_SQLSRV_ERROR_INVALID_PARAMETER_DIRECTION, index + 1 ) {
throw ss::SSException();
}
CHECK_CUSTOM_ERROR(!Z_ISREF_P(var_or_val) && (direction == SQL_PARAM_OUTPUT || direction == SQL_PARAM_INPUT_OUTPUT), stmt, SS_SQLSRV_ERROR_PARAM_VAR_NOT_REF, index + 1) {
throw ss::SSException();
}
}
else {
direction = SQL_PARAM_INPUT;
}
// extract the php type and encoding from the 3rd parameter
if( zend_hash_move_forward( param_ht ) == SUCCESS && zend_hash_get_current_data( param_ht, (void**) &temp ) == SUCCESS &&
Z_TYPE_PP( temp ) != IS_NULL ) {
if (zend_hash_move_forward(param_ht) == SUCCESS && (temp = zend_hash_get_current_data(param_ht)) != NULL &&
Z_TYPE_P( temp ) != IS_NULL ) {
php_type_param_was_null = false;
sqlsrv_phptype sqlsrv_phptype;
CHECK_CUSTOM_ERROR( Z_TYPE_PP( temp ) != IS_LONG, stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, index + 1 ) {
CHECK_CUSTOM_ERROR( Z_TYPE_P( temp ) != IS_LONG, stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE, index + 1 ) {
throw ss::SSException();
}
sqlsrv_phptype.value = Z_LVAL_PP( temp );
sqlsrv_phptype.value = Z_LVAL_P( temp );
CHECK_CUSTOM_ERROR( !is_valid_sqlsrv_phptype( sqlsrv_phptype ), stmt, SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE,
index + 1 ) {
@ -1985,7 +1962,13 @@ void parse_param_array( ss_sqlsrv_stmt* stmt, __inout zval* param_array, unsigne
else {
php_type_param_was_null = true;
php_out_type = zend_to_sqlsrv_phptype[ Z_TYPE_PP( var_or_val ) ];
if (Z_ISREF_P(var_or_val)){
php_out_type = zend_to_sqlsrv_phptype[Z_TYPE_P(Z_REFVAL_P(var_or_val))];
}
else{
php_out_type = zend_to_sqlsrv_phptype[Z_TYPE_P(var_or_val)];
}
encoding = stmt->encoding();
if( encoding == SQLSRV_ENCODING_DEFAULT ) {
encoding = stmt->conn->encoding();
@ -1993,17 +1976,17 @@ void parse_param_array( ss_sqlsrv_stmt* stmt, __inout zval* param_array, unsigne
}
// get the server type, column size/precision and the decimal digits if provided
if( zend_hash_move_forward( param_ht ) == SUCCESS && zend_hash_get_current_data( param_ht, (void**) &temp ) == SUCCESS &&
Z_TYPE_PP( temp ) != IS_NULL ) {
if (zend_hash_move_forward(param_ht) == SUCCESS && (temp = zend_hash_get_current_data(param_ht)) != NULL &&
Z_TYPE_P( temp ) != IS_NULL ) {
sql_type_param_was_null = false;
CHECK_CUSTOM_ERROR( Z_TYPE_PP( temp ) != IS_LONG, stmt, SQLSRV_ERROR_INVALID_PARAMETER_SQLTYPE, index + 1 ) {
CHECK_CUSTOM_ERROR( Z_TYPE_P( temp ) != IS_LONG, stmt, SQLSRV_ERROR_INVALID_PARAMETER_SQLTYPE, index + 1 ) {
throw ss::SSException();
}
sqlsrv_sql_type.value = Z_LVAL_PP( temp );
sqlsrv_sql_type.value = Z_LVAL_P( temp );
// since the user supplied this type, make sure it's valid
CHECK_CUSTOM_ERROR( !is_valid_sqlsrv_sqltype( sqlsrv_sql_type ), stmt, SQLSRV_ERROR_INVALID_PARAMETER_SQLTYPE,
@ -2012,7 +1995,7 @@ void parse_param_array( ss_sqlsrv_stmt* stmt, __inout zval* param_array, unsigne
throw ss::SSException();
}
bool size_okay = determine_column_size_or_precision( stmt, sqlsrv_sql_type, &column_size, &decimal_digits );
bool size_okay = determine_column_size_or_precision(stmt, sqlsrv_sql_type, &column_size, &decimal_digits);
CHECK_CUSTOM_ERROR( !size_okay, stmt, SS_SQLSRV_ERROR_INVALID_PARAMETER_PRECISION, index + 1 ) {
@ -2037,7 +2020,9 @@ void parse_param_array( ss_sqlsrv_stmt* stmt, __inout zval* param_array, unsigne
int encoding;
sqlsrv_phptype sqlsrv_phptype;
sqlsrv_phptype = determine_sqlsrv_php_type( stmt, sql_type, column_size, true );
sqlsrv_phptype = determine_sqlsrv_php_type( stmt, sql_type, (SQLUINTEGER)column_size, true );
// we DIE here since everything should have been validated already and to return the user an error
// for our own logic error would be confusing/misleading.
SQLSRV_ASSERT( sqlsrv_phptype.typeinfo.type != PHPTYPE_INVALID, "An invalid php type was returned with (supposed) "
@ -2132,23 +2117,22 @@ bool is_valid_sqlsrv_sqltype( sqlsrv_sqltype sql_type )
// verify an encoding given to type_and_encoding by looking through the list
// of standard encodings created at module initialization time
bool verify_and_set_encoding( const char* encoding_string, __out sqlsrv_phptype& phptype_encoding TSRMLS_DC )
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 = zend_hash_get_current_data( g_ss_encodings_ht, (void**) &encoding );
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;
}
@ -2157,12 +2141,8 @@ bool verify_and_set_encoding( const char* encoding_string, __out sqlsrv_phptype&
// into a sqlsrv_sqltype bit fields (see php_sqlsrv.h).
void type_and_size_calc( INTERNAL_FUNCTION_PARAMETERS, int type )
{
SQLSRV_UNUSED( return_value_used );
SQLSRV_UNUSED( this_ptr );
SQLSRV_UNUSED( return_value_ptr );
char* size_p = NULL;
int size_len = 0;
size_t size_len = 0;
long size = 0;
if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s", &size_p, &size_len ) == FAILURE ) {
@ -2205,12 +2185,8 @@ void type_and_size_calc( INTERNAL_FUNCTION_PARAMETERS, int type )
// field. encodes these into a sqlsrv_sqltype structure (see php_sqlsrv.h)
void type_and_precision_calc( INTERNAL_FUNCTION_PARAMETERS, int type )
{
SQLSRV_UNUSED( return_value_used );
SQLSRV_UNUSED( this_ptr );
SQLSRV_UNUSED( return_value_ptr );
long prec = SQLSRV_INVALID_PRECISION;
long scale = SQLSRV_INVALID_SCALE;
zend_long prec = SQLSRV_INVALID_PRECISION;
zend_long scale = SQLSRV_INVALID_SCALE;
if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "|ll", &prec, &scale ) == FAILURE ) {
@ -2244,14 +2220,11 @@ void type_and_precision_calc( INTERNAL_FUNCTION_PARAMETERS, int type )
// encodes the type and encoding into a sqlsrv_phptype structure (see php_sqlsrv.h)
void type_and_encoding( INTERNAL_FUNCTION_PARAMETERS, int type )
{
SQLSRV_UNUSED( return_value_used );
SQLSRV_UNUSED( this_ptr );
SQLSRV_UNUSED( return_value_ptr );
SQLSRV_ASSERT(( type == SQLSRV_PHPTYPE_STREAM || type == SQLSRV_PHPTYPE_STRING ), "type_and_encoding: Invalid type passed." );
char* encoding_param;
int encoding_param_len = 0;
size_t encoding_param_len = 0;
// set the default encoding values to invalid so that
// if the encoding isn't validated, it will return the invalid setting.

View file

@ -3,7 +3,7 @@
//
// Contents: Version resource
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.0 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -59,9 +59,9 @@ BEGIN
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "Comments", "This product includes PHP software that is freely available from http://www.php.net/software/. © 1997-2009 The PHP Group. All rights reserved.\0"
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

@ -5,7 +5,7 @@
//
// Comments: Mostly error handling and some type handling
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -36,14 +36,14 @@ char log_msg[ LOG_MSG_SIZE ];
SQLCHAR INTERNAL_FORMAT_ERROR[] = "An internal error occurred. FormatMessage failed writing an error message.";
// *** internal functions ***
void copy_error_to_zval( zval** error_z, sqlsrv_error_const* error, zval** reported_chain, zval** ignored_chain,
void copy_error_to_zval( zval* error_z, sqlsrv_error_const* error, zval* reported_chain, zval* ignored_chain,
bool warning TSRMLS_DC );
bool ignore_warning( char* sql_state, int native_code TSRMLS_DC );
bool handle_errors_and_warnings( sqlsrv_context& ctx, zval** reported_chain, zval** ignored_chain, logging_severity log_severity,
bool handle_errors_and_warnings( sqlsrv_context& ctx, zval* reported_chain, zval* ignored_chain, logging_severity log_severity,
unsigned int sqlsrv_error_code, bool warning, va_list* print_args TSRMLS_DC );
int sqlsrv_merge_zend_hash_dtor( void* dest TSRMLS_DC );
bool sqlsrv_merge_zend_hash( __inout zval* dest_z, zval const* src_z TSRMLS_DC );
int sqlsrv_merge_zend_hash_dtor( zval* dest TSRMLS_DC );
bool sqlsrv_merge_zend_hash( _Inout_ zval* dest_z, zval const* src_z TSRMLS_DC );
}
@ -300,8 +300,8 @@ ss_error SS_ERRORS[] = {
{
SQLSRV_ERROR_DRIVER_NOT_INSTALLED,
{ IMSSP, (SQLCHAR*) "This extension requires the Microsoft ODBC Driver 11 for SQL Server. "
"Access the following URL to download the ODBC Driver 11 for SQL Server for %1!s!: "
{ IMSSP, (SQLCHAR*) "This extension requires the Microsoft ODBC Driver 11 or 13 for SQL Server. "
"Access the following URL to download the ODBC Driver 11 or 13 for SQL Server for %1!s!: "
"http://go.microsoft.com/fwlink/?LinkId=163712", -49, true }
},
@ -360,6 +360,12 @@ ss_error SS_ERRORS[] = {
SQLSRV_ERROR_INVALID_BUFFER_LIMIT,
{ IMSSP, (SQLCHAR*) "Setting for " INI_BUFFERED_QUERY_LIMIT " was non-int or non-positive.", -60, false }
},
{
SS_SQLSRV_ERROR_PARAM_VAR_NOT_REF,
{ IMSSP, (SQLCHAR*)"Variable parameter %1!d! not passed by reference (prefaced with an &). "
"Output or bidirectional variable parameters (SQLSRV_PARAM_OUT and SQLSRV_PARAM_INOUT) passed to sqlsrv_prepare or sqlsrv_query should be passed by reference, not by value."
, -61, true }
},
// internal warning definitions
{
@ -368,13 +374,14 @@ ss_error SS_ERRORS[] = {
},
// terminate the list of errors/warnings
{ -1, {} }
{ UINT_MAX, {} }
};
sqlsrv_error_const* get_error_message( unsigned int sqlsrv_error_code ) {
sqlsrv_error_const *error_message = NULL;
int zr = zend_hash_index_find( g_ss_errors_ht, sqlsrv_error_code, reinterpret_cast<void**>( &error_message ));
int zr = (error_message = reinterpret_cast<sqlsrv_error_const*>(zend_hash_index_find_ptr( g_ss_errors_ht, sqlsrv_error_code))) != NULL ? SUCCESS : FAILURE;
if( zr == FAILURE ) {
DIE( "get_error_message: zend_hash_index_find returned failure for sqlsrv_error_code = %1!d!", sqlsrv_error_code );
}
@ -451,65 +458,41 @@ bool ss_error_handler(sqlsrv_context& ctx, unsigned int sqlsrv_error_code, bool
PHP_FUNCTION( sqlsrv_errors )
{
SQLSRV_UNUSED( return_value_used );
SQLSRV_UNUSED( this_ptr );
SQLSRV_UNUSED( return_value_ptr );
SQLSRV_UNUSED( execute_data );
long flags = SQLSRV_ERR_ALL;
full_mem_check(MEMCHECK_SILENT);
zend_long flags = SQLSRV_ERR_ALL;
LOG_FUNCTION( "sqlsrv_errors" );
if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "|l", &flags ) == FAILURE ) {
LOG(SEV_ERROR, "An invalid parameter was passed to %1!s!.", _FN_ );
RETURN_FALSE;
}
if( flags == SQLSRV_ERR_ALL ) {
int result;
zval_auto_ptr both_z;
MAKE_STD_ZVAL( both_z );
result = array_init( both_z );
if( result == FAILURE ) {
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();
}
zval_ptr_dtor( &return_value );
*return_value_ptr = both_z;
both_z.transferred();
}
else if( flags == SQLSRV_ERR_WARNINGS ) {
zval_ptr_dtor( &return_value );
*return_value_ptr = SQLSRV_G( warnings );
zval_add_ref( &SQLSRV_G( warnings ));
}
else {
zval_ptr_dtor( &return_value );
*return_value_ptr = SQLSRV_G( errors );
zval_add_ref( &SQLSRV_G( errors ));
}
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 )) {
zval_ptr_dtor(&err_z);
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 )) {
zval_ptr_dtor(&err_z);
RETURN_FALSE;
}
}
if( zend_hash_num_elements( Z_ARRVAL_P( &err_z )) == 0 ) {
zval_ptr_dtor(&err_z);
RETURN_NULL();
}
RETURN_ZVAL( &err_z, 1, 1 );
}
// sqlsrv_configure( string $setting, mixed $value )
@ -529,14 +512,12 @@ PHP_FUNCTION( sqlsrv_errors )
PHP_FUNCTION( sqlsrv_configure )
{
SQLSRV_UNUSED( return_value_used );
SQLSRV_UNUSED( this_ptr );
SQLSRV_UNUSED( return_value_ptr );
SQLSRV_UNUSED( execute_data );
LOG_FUNCTION( "sqlsrv_configure" );
char* option;
int option_len;
size_t option_len;
zval* value_z;
sqlsrv_context_auto_ptr error_ctx;
@ -572,7 +553,7 @@ PHP_FUNCTION( sqlsrv_configure )
throw ss::SSException();
}
long severity_mask = Z_LVAL_P( value_z );
zend_long severity_mask = Z_LVAL_P( value_z );
// make sure they can't use 0 to shut off the masking in the severity
if( severity_mask < SEV_ALL || severity_mask == 0 || severity_mask > (SEV_NOTICE + SEV_ERROR + SEV_WARNING) ) {
RETURN_FALSE;
@ -591,7 +572,7 @@ PHP_FUNCTION( sqlsrv_configure )
throw ss::SSException();
}
long subsystem_mask = Z_LVAL_P( value_z );
zend_long subsystem_mask = Z_LVAL_P( value_z );
if( subsystem_mask < LOG_ALL || subsystem_mask > (LOG_INIT + LOG_CONN + LOG_STMT + LOG_UTIL) ) {
RETURN_FALSE;
@ -609,7 +590,7 @@ PHP_FUNCTION( sqlsrv_configure )
throw ss::SSException();
}
long buffered_query_limit = Z_LVAL_P( value_z );
zend_long buffered_query_limit = Z_LVAL_P( value_z );
CHECK_CUSTOM_ERROR( buffered_query_limit < 0, error_ctx, SQLSRV_ERROR_INVALID_BUFFER_LIMIT, _FN_ ) {
@ -653,12 +634,10 @@ PHP_FUNCTION( sqlsrv_configure )
PHP_FUNCTION( sqlsrv_get_config )
{
SQLSRV_UNUSED( return_value_used );
SQLSRV_UNUSED( this_ptr );
SQLSRV_UNUSED( return_value_ptr );
SQLSRV_UNUSED( execute_data );
char* option = NULL;
int option_len;
size_t option_len;
sqlsrv_context_auto_ptr error_ctx;
LOG_FUNCTION( "sqlsrv_get_config" );
@ -715,50 +694,49 @@ PHP_FUNCTION( sqlsrv_get_config )
namespace {
void copy_error_to_zval( zval** error_z, sqlsrv_error_const* error, zval** reported_chain, zval** ignored_chain,
void copy_error_to_zval( zval* error_z, sqlsrv_error_const* error, zval* reported_chain, zval* ignored_chain,
bool warning TSRMLS_DC )
{
MAKE_STD_ZVAL( *error_z );
if( array_init( *error_z ) == FAILURE ) {
if( array_init( error_z ) == FAILURE ) {
DIE( "Fatal error during error processing" );
}
// sqlstate
zval_auto_ptr temp;
MAKE_STD_ZVAL( temp );
ZVAL_STRINGL( temp, reinterpret_cast<char*>( error->sqlstate ), SQL_SQLSTATE_SIZE, 1 );
zval_add_ref( &temp );
if( add_next_index_zval( *error_z, temp ) == FAILURE ) {
zval temp;
ZVAL_UNDEF(&temp);
core::sqlsrv_zval_stringl( &temp, reinterpret_cast<char*>( error->sqlstate ), SQL_SQLSTATE_SIZE );
//TODO: reference?
Z_TRY_ADDREF_P( &temp );
if( add_next_index_zval( error_z, &temp ) == FAILURE ) {
DIE( "Fatal error during error processing" );
}
if( add_assoc_zval( *error_z, "SQLSTATE", temp ) == FAILURE ) {
if( add_assoc_zval( error_z, "SQLSTATE", &temp ) == FAILURE ) {
DIE( "Fatal error during error processing" );
}
temp.transferred();
// native_code
if( add_next_index_long( *error_z, error->native_code ) == FAILURE ) {
if( add_next_index_long( error_z, error->native_code ) == FAILURE ) {
DIE( "Fatal error during error processing" );
}
if( add_assoc_long( *error_z, "code", error->native_code ) == FAILURE ) {
if( add_assoc_long( error_z, "code", error->native_code ) == FAILURE ) {
DIE( "Fatal error during error processing" );
}
// native_message
MAKE_STD_ZVAL( temp );
ZVAL_STRING( temp, reinterpret_cast<char*>( error->native_message), 1 );
zval_add_ref( &temp );
if( add_next_index_zval( *error_z, temp ) == FAILURE ) {
ZVAL_UNDEF(&temp);
ZVAL_STRING( &temp, reinterpret_cast<char*>( error->native_message ) );
//TODO: reference?
Z_TRY_ADDREF_P(&temp);
if( add_next_index_zval( error_z, &temp ) == FAILURE ) {
DIE( "Fatal error during error processing" );
}
if( add_assoc_zval( *error_z, "message", temp ) == FAILURE ) {
if( add_assoc_zval( error_z, "message", &temp ) == FAILURE ) {
DIE( "Fatal error during error processing" );
}
temp.transferred();
// If it is an error or if warning_return_as_errors is true than
// add the error or warning to the reported_chain.
@ -766,17 +744,17 @@ void copy_error_to_zval( zval** error_z, sqlsrv_error_const* error, zval** repor
{
// if the warning is part of the ignored warning list than
// add to the ignored chain if the ignored chain is not null.
if( warning && ignore_warning( reinterpret_cast<char*>( error->sqlstate ), error->native_code TSRMLS_CC ) &&
if( warning && ignore_warning( reinterpret_cast<char*>(error->sqlstate), error->native_code TSRMLS_CC ) &&
ignored_chain != NULL ) {
if( add_next_index_zval( *ignored_chain, *error_z ) == FAILURE ) {
if( add_next_index_zval( ignored_chain, error_z ) == FAILURE ) {
DIE( "Fatal error during error processing" );
}
}
else {
// It is either an error or a warning which should not be ignored.
if( add_next_index_zval( *reported_chain, *error_z ) == FAILURE ) {
if( add_next_index_zval( reported_chain, error_z ) == FAILURE ) {
DIE( "Fatal error during error processing" );
}
}
@ -786,45 +764,46 @@ void copy_error_to_zval( zval** error_z, sqlsrv_error_const* error, zval** repor
// It is a warning with warning_return_as_errors as false, so simply add it to the ignored_chain list
if( ignored_chain != NULL ) {
if( add_next_index_zval( *ignored_chain, *error_z ) == FAILURE ) {
if( add_next_index_zval( ignored_chain, error_z ) == FAILURE ) {
DIE( "Fatal error during error processing" );
}
}
}
}
bool handle_errors_and_warnings( sqlsrv_context& ctx, zval** reported_chain, zval** ignored_chain, logging_severity log_severity,
bool handle_errors_and_warnings( sqlsrv_context& ctx, zval* reported_chain, zval* ignored_chain, logging_severity log_severity,
unsigned int sqlsrv_error_code, bool warning, va_list* print_args TSRMLS_DC )
{
bool result = true;
bool errors_ignored = false;
int prev_reported_cnt = 0;
size_t prev_reported_cnt = 0;
bool reported_chain_was_null = false;
bool ignored_chain_was_null = false;
int zr = SUCCESS;
zval* error_z = NULL;
zval error_z;
ZVAL_UNDEF(&error_z);
sqlsrv_error_auto_ptr error;
// array of reported errors
if( Z_TYPE_P( *reported_chain ) == IS_NULL ) {
if( Z_TYPE_P( reported_chain ) == IS_NULL ) {
reported_chain_was_null = true;
zr = array_init( *reported_chain );
zr = array_init( reported_chain );
if( zr == FAILURE ) {
DIE( "Fatal error in handle_errors_and_warnings" );
}
}
else {
prev_reported_cnt = zend_hash_num_elements( Z_ARRVAL_PP( reported_chain ));
prev_reported_cnt = zend_hash_num_elements( Z_ARRVAL_P( reported_chain ));
}
// array of ignored errors
if( ignored_chain != NULL ) {
if( Z_TYPE_P( *ignored_chain ) == IS_NULL ) {
if( Z_TYPE_P( ignored_chain ) == IS_NULL ) {
ignored_chain_was_null = true;
zr = array_init( *ignored_chain );
zr = array_init( ignored_chain );
if( zr == FAILURE ) {
DIE( "Fatal error in handle_errors_and_warnings" );
}
@ -854,7 +833,7 @@ bool handle_errors_and_warnings( sqlsrv_context& ctx, zval** reported_chain, zva
if( SQLSRV_G( warnings_return_as_errors ) ) {
if( zend_hash_num_elements( Z_ARRVAL_PP( reported_chain )) > prev_reported_cnt ) {
if( zend_hash_num_elements( Z_ARRVAL_P( reported_chain )) > prev_reported_cnt ) {
// We actually added some errors
errors_ignored = false;
@ -863,15 +842,15 @@ bool handle_errors_and_warnings( sqlsrv_context& ctx, zval** reported_chain, zva
}
// if the error array came in as NULL and didn't have anything added to it, return it as NULL
if( reported_chain_was_null && zend_hash_num_elements( Z_ARRVAL_PP( reported_chain )) == 0 ) {
zend_hash_destroy( Z_ARRVAL_PP( reported_chain ));
FREE_HASHTABLE( Z_ARRVAL_PP( reported_chain ));
ZVAL_NULL( *reported_chain );
if( reported_chain_was_null && zend_hash_num_elements( Z_ARRVAL_P( reported_chain )) == 0 ) {
zend_hash_destroy( Z_ARRVAL_P( reported_chain ));
FREE_HASHTABLE( Z_ARRVAL_P( reported_chain ));
ZVAL_NULL( reported_chain );
}
if( ignored_chain != NULL && ignored_chain_was_null && zend_hash_num_elements( Z_ARRVAL_PP( ignored_chain )) == 0 ) {
zend_hash_destroy( Z_ARRVAL_PP( ignored_chain ));
FREE_HASHTABLE( Z_ARRVAL_PP( ignored_chain ));
ZVAL_NULL( *ignored_chain );
if( ignored_chain != NULL && ignored_chain_was_null && zend_hash_num_elements( Z_ARRVAL_P( ignored_chain )) == 0 ) {
zend_hash_destroy( Z_ARRVAL_P( ignored_chain ));
FREE_HASHTABLE( Z_ARRVAL_P( ignored_chain ));
ZVAL_NULL( ignored_chain );
}
// If it was an error instead of a warning than we always return errors_ignored = false.
@ -882,44 +861,35 @@ bool handle_errors_and_warnings( sqlsrv_context& ctx, zval** reported_chain, zva
// 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;
void* error_v = NULL;
if( zend_hash_get_current_data( g_ss_warnings_to_ignore_ht, (void**) &error_v ) == FAILURE ) {
return false;
}
sqlsrv_error* error = static_cast<sqlsrv_error*>( error_v );
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_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;
}
} ZEND_HASH_FOREACH_END();
return false;
}
int sqlsrv_merge_zend_hash_dtor( void* dest TSRMLS_DC )
{
#if defined(ZTS)
SQLSRV_UNUSED( tsrm_ls );
#endif
zval_ptr_dtor( reinterpret_cast<zval**>( &dest ));
int sqlsrv_merge_zend_hash_dtor( zval* dest TSRMLS_DC )
{
zval_ptr_dtor( dest );
return ZEND_HASH_APPLY_REMOVE;
}
// sqlsrv_merge_zend_hash
// merge a source hash into a dest hash table and return any errors.
bool sqlsrv_merge_zend_hash( __inout zval* dest_z, zval const* src_z TSRMLS_DC )
bool sqlsrv_merge_zend_hash( _Inout_ zval* dest_z, zval const* src_z TSRMLS_DC )
{
#if defined(ZTS)
SQLSRV_UNUSED( tsrm_ls );
#endif
if( Z_TYPE_P( dest_z ) != IS_ARRAY && Z_TYPE_P( dest_z ) != IS_NULL ) DIE( "dest_z must be an array or null" );
if( Z_TYPE_P( src_z ) != IS_ARRAY && Z_TYPE_P( src_z ) != IS_NULL ) DIE( "src_z must be an array or null" );
@ -928,29 +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 ) ) {
void* value_v;
zval* 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;
}
result = zend_hash_get_current_data( src_ht, (void**) &value_v );
if( result == FAILURE ) {
zend_hash_apply( Z_ARRVAL_P( dest_z ), sqlsrv_merge_zend_hash_dtor TSRMLS_CC );
return false;
}
value_z = *(static_cast<zval**>( value_v ));
result = add_next_index_zval( dest_z, value_z );
int result = add_next_index_zval( dest_z, value_z );
if( result == FAILURE ) {
zend_hash_apply( Z_ARRVAL_P( dest_z ), sqlsrv_merge_zend_hash_dtor TSRMLS_CC );
return false;
}
zval_add_ref( &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;
}

View file

@ -2,7 +2,7 @@
// File: version.h
// Contents: Version number constants
//
// Microsoft Drivers 3.2 for PHP for SQL Server
// Microsoft Drivers 4.1 for PHP for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
@ -16,11 +16,11 @@
// IN THE SOFTWARE.
//---------------------------------------------------------------------------------------------------------------------------------
#define VER_FILEVERSION_STR "3.2.1.0"
#define _FILEVERSION 3,2,1,0
#define SQLVERSION_MAJOR 3
#define SQLVERSION_MINOR 2
#define SQLVERSION_MMDD 1
#define VER_FILEVERSION_STR "4.1.0.0"
#define _FILEVERSION 4,1,0,0
#define SQLVERSION_MAJOR 4
#define SQLVERSION_MINOR 1
#define SQLVERSION_MMDD 0
#define SQLVERSION_REVISION 0