diff --git a/LICENSE b/LICENSE index a39c89d3..d332c60c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright(c) 2015 Microsoft Corporation +Copyright(c) 2016 Microsoft Corporation All rights reserved. MIT License diff --git a/README.md b/README.md index 9acf5ac1..f6fd78a5 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,23 @@ 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. -This preview contains the SQLSRV and PDO_SQLSRV drivers for PHP 7 with limitations (see Limitations below for details). Upcoming release(s) will contain more functionality, bug fixes, and more (see Plans below for more details). +This preview 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 +May 3, 2016 (4.0.4): The quality of SQLSRV and PDO_SQLSRV is improved and includes some bug fixes: +- Fixed retrieving stream data and metadata. +- Fixed issue with bind stream parameters. +- Fixed issue with retrieval in error case when trying to retrieve a non-streamble data type with SQLSRV_SQLTYPE_STREAM option +- Fixed issue with querying after another query with empty array of parameters. +- Fixed issue with retrieving integers as output parameter in SQLSRV 64-bit. +- Fixed issue scrollable statement option in SQLSRV_PDO 64-bit. +- Improved handling closed connection and statement resources. +- Fixed issue with binding bit parameter. +- Fix for The $driver_options (for specifying encoding) is included in PDOStatement::bindParam is included in PHP 7.0.6 release. + April 12, 2016 (4.0.3): The PDO_SQLSRV driver (32-bit and 64-bit) is now available. For the SQLSRV driver, we also have a few bug fixes to share: - Fixed ability to fetch a user defined object into a class - Fixed issue with re-preparing the same statement with referenced datetime parameters @@ -47,7 +58,7 @@ You must first be able to build PHP 7 without including these extensions. For h 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. -This software has been compiled and tested under PHP 7.0.5 using the Visual C++ 2015 compiler. +This software has been compiled and tested under PHP 7.0.6 using the Visual C++ 2015 compiler. ## Install @@ -69,18 +80,14 @@ For samples, please see the sample folder. For setup instructions, see [here] [ ## Limitations -This preview contains the PHP 7 port of the SQLSRV and PDO_SQLSRV drivers. The focus was on basic functionality and does not provide backwards compatibility with PHP 5. The following items have known issues: +This preview contains the PHP 7 port of the SQLSRV and PDO_SQLSRV drivers, and does not provide backwards compatibility with PHP 5. The following items have known issues: SQLSRV: -- Retrieving stream data and metadata -- Handle UTF8 strings -- Memory management +- Memory management. -SQLSRV 64-bit only: -- Retrieving integers as output parameters +PDO_SQLSRV 64-bit only: +- Retrieving integers as output parameters. -PDO_SQLSRV: -- The $driver_options (for specifying encoding) in PDOStatement::bindParam does not work due to a bug in the PDO extension source code. A fix for this bug now [exists](https://github.com/php/php-src/commit/5b8d0dc6ae01907d35ea51c061addedfe81e4e1f) but it hasn't made it into an official PHP release yet. ## Future Plans diff --git a/binaries/x64/php_pdo_sqlsrv_7_nts.dll b/binaries/x64/php_pdo_sqlsrv_7_nts.dll index 1b6fbbcc..80023e5f 100644 Binary files a/binaries/x64/php_pdo_sqlsrv_7_nts.dll and b/binaries/x64/php_pdo_sqlsrv_7_nts.dll differ diff --git a/binaries/x64/php_pdo_sqlsrv_7_ts.dll b/binaries/x64/php_pdo_sqlsrv_7_ts.dll index 5be9588a..7e601fa1 100644 Binary files a/binaries/x64/php_pdo_sqlsrv_7_ts.dll and b/binaries/x64/php_pdo_sqlsrv_7_ts.dll differ diff --git a/binaries/x64/php_sqlsrv_7_nts.dll b/binaries/x64/php_sqlsrv_7_nts.dll index e5bcc533..8e77e8ec 100644 Binary files a/binaries/x64/php_sqlsrv_7_nts.dll and b/binaries/x64/php_sqlsrv_7_nts.dll differ diff --git a/binaries/x64/php_sqlsrv_7_ts.dll b/binaries/x64/php_sqlsrv_7_ts.dll index f73ed86f..8d503011 100644 Binary files a/binaries/x64/php_sqlsrv_7_ts.dll and b/binaries/x64/php_sqlsrv_7_ts.dll differ diff --git a/binaries/x86/php_pdo_sqlsrv_7_nts.dll b/binaries/x86/php_pdo_sqlsrv_7_nts.dll index 75872d44..7470eac5 100644 Binary files a/binaries/x86/php_pdo_sqlsrv_7_nts.dll and b/binaries/x86/php_pdo_sqlsrv_7_nts.dll differ diff --git a/binaries/x86/php_pdo_sqlsrv_7_ts.dll b/binaries/x86/php_pdo_sqlsrv_7_ts.dll index a92fe58a..56f13c3d 100644 Binary files a/binaries/x86/php_pdo_sqlsrv_7_ts.dll and b/binaries/x86/php_pdo_sqlsrv_7_ts.dll differ diff --git a/binaries/x86/php_sqlsrv_7_nts.dll b/binaries/x86/php_sqlsrv_7_nts.dll index 726e08ab..4e699a42 100644 Binary files a/binaries/x86/php_sqlsrv_7_nts.dll and b/binaries/x86/php_sqlsrv_7_nts.dll differ diff --git a/binaries/x86/php_sqlsrv_7_ts.dll b/binaries/x86/php_sqlsrv_7_ts.dll index 3569ce53..efc0ba90 100644 Binary files a/binaries/x86/php_sqlsrv_7_ts.dll and b/binaries/x86/php_sqlsrv_7_ts.dll differ diff --git a/pdo_sqlsrv/core_conn.cpp b/pdo_sqlsrv/core_conn.cpp index 89f213b4..825393e7 100644 --- a/pdo_sqlsrv/core_conn.cpp +++ b/pdo_sqlsrv/core_conn.cpp @@ -130,9 +130,9 @@ sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_ // We only support UTF-8 encoding for connection string. // Convert our UTF-8 connection string to UTF-16 before connecting with SQLDriverConnnectW - wconn_len = (unsigned int) (conn_str.length() + 1) * sizeof( wchar_t ); + wconn_len = static_cast( conn_str.length() + 1 ) * sizeof( wchar_t ); - wconn_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_UTF8, conn_str.c_str(), (unsigned int) conn_str.length(), &wconn_len ); + wconn_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_UTF8, conn_str.c_str(), static_cast( conn_str.length() ), &wconn_len ); CHECK_CUSTOM_ERROR( wconn_string == NULL, conn, SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE, get_last_error_message() ) { throw core::CoreException(); diff --git a/pdo_sqlsrv/core_sqlsrv.h b/pdo_sqlsrv/core_sqlsrv.h index 6858b1c9..2563ee70 100644 --- a/pdo_sqlsrv/core_sqlsrv.h +++ b/pdo_sqlsrv/core_sqlsrv.h @@ -1318,7 +1318,7 @@ void core_sqlsrv_get_field(sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_p bool core_sqlsrv_has_any_result( sqlsrv_stmt* stmt TSRMLS_DC ); void core_sqlsrv_next_result( sqlsrv_stmt* stmt TSRMLS_DC, bool finalize_output_params = true, bool throw_on_errors = true ); void core_sqlsrv_post_param( sqlsrv_stmt* stmt, zend_ulong paramno, zval* param_z TSRMLS_DC ); -void core_sqlsrv_set_scrollable( sqlsrv_stmt* stmt, SQLULEN cursor_type 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 ); @@ -2061,6 +2061,9 @@ 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) diff --git a/pdo_sqlsrv/core_stmt.cpp b/pdo_sqlsrv/core_stmt.cpp index 16f0a950..e727262f 100644 --- a/pdo_sqlsrv/core_stmt.cpp +++ b/pdo_sqlsrv/core_stmt.cpp @@ -348,8 +348,8 @@ void core_sqlsrv_bind_param(sqlsrv_stmt* stmt, SQLUSMALLINT param_num, SQLSMALLI 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); + 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. @@ -422,7 +422,7 @@ void core_sqlsrv_bind_param(sqlsrv_stmt* stmt, SQLUSMALLINT param_num, SQLSMALLI // 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, (unsigned int)param_num, param_z, encoding, column_size, decimal_digits TSRMLS_CC ); + default_sql_size_and_scale( stmt, static_cast( param_num ), param_z, encoding, column_size, decimal_digits TSRMLS_CC ); } // determine the ODBC C type @@ -442,13 +442,15 @@ void core_sqlsrv_bind_param(sqlsrv_stmt* stmt, SQLUSMALLINT param_num, SQLSMALLI 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 = ¶m_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_ref, int(param_num), zval_was_bool ); + sqlsrv_output_param output_param( param_ref, static_cast( param_num ), zval_was_bool ); save_output_param_for_later( stmt, output_param TSRMLS_CC ); } } @@ -456,11 +458,11 @@ void core_sqlsrv_bind_param(sqlsrv_stmt* stmt, SQLUSMALLINT param_num, SQLSMALLI case IS_DOUBLE: { buffer = ¶m_z->value; - buffer_len = sizeof(Z_DVAL_P(param_z)); + 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_ref, int(param_num), false ); + sqlsrv_output_param output_param(param_ref, static_cast( param_num ), false ); save_output_param_for_later( stmt, output_param TSRMLS_CC ); } } @@ -472,7 +474,7 @@ void core_sqlsrv_bind_param(sqlsrv_stmt* stmt, SQLUSMALLINT param_num, SQLSMALLI if( direction == SQL_PARAM_INPUT && encoding == CP_UTF8 ) { zval wbuffer_z; - ZVAL_NULL(&wbuffer_z); + ZVAL_NULL( &wbuffer_z ); bool converted = convert_input_param_to_utf16( param_z, &wbuffer_z ); CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_INPUT_PARAM_ENCODING_TRANSLATE, @@ -519,12 +521,12 @@ void core_sqlsrv_bind_param(sqlsrv_stmt* stmt, SQLUSMALLINT param_num, SQLSMALLI 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_ref, encoding, param_num, (SQLUINTEGER)buffer_len ); + sqlsrv_output_param output_param( param_ref, encoding, param_num, static_cast( 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 @@ -552,7 +554,7 @@ void core_sqlsrv_bind_param(sqlsrv_stmt* stmt, SQLUSMALLINT param_num, SQLSMALLI 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( param_num ); - Z_TRY_ADDREF_P(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; } @@ -702,6 +704,8 @@ void core_sqlsrv_execute( sqlsrv_stmt* stmt TSRMLS_DC, const char* sql, int sql_ finalize_output_parameters( stmt TSRMLS_CC ); } + // stream parameters are sent, clean the Hashtable + zend_hash_clean( Z_ARRVAL( stmt->param_streams )); } catch( core::CoreException& e ) { @@ -935,7 +939,7 @@ void core_sqlsrv_get_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_ 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( (SQLINTEGER)sql_field_type, (SQLUINTEGER)sql_field_len, prefer_string ); + sqlsrv_php_type = stmt->sql_type_to_php_type( static_cast( sql_field_type ), static_cast( sql_field_len ), prefer_string ); } // Verify that we have an acceptable type to convert. @@ -1060,7 +1064,7 @@ void core_sqlsrv_post_param( sqlsrv_stmt* stmt, zend_ulong param_num, zval* para } //Calls SQLSetStmtAttr to set a cursor. -void core_sqlsrv_set_scrollable( sqlsrv_stmt* stmt, SQLULEN cursor_type TSRMLS_DC ) +void core_sqlsrv_set_scrollable( sqlsrv_stmt* stmt, unsigned long cursor_type TSRMLS_DC ) { try { @@ -1139,7 +1143,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, (long)Z_LVAL_P( value_z ) TSRMLS_CC ); + core_sqlsrv_set_query_timeout( stmt, static_cast( Z_LVAL_P( value_z )) TSRMLS_CC ); } catch( core::CoreException& ) { throw; @@ -1242,7 +1246,7 @@ bool core_sqlsrv_send_stream_packet( sqlsrv_stmt* stmt TSRMLS_DC ) throw core::CoreException(); } - stmt->current_stream_read += (unsigned int)read; + stmt->current_stream_read += static_cast( 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 @@ -1256,7 +1260,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, int(read), wbuffer, int(sizeof( wbuffer ) / sizeof( wchar_t ))); + buffer, static_cast( read ), wbuffer, static_cast( 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 @@ -1272,7 +1276,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, int(read + new_read), wbuffer, int(sizeof( wbuffer ) / sizeof( wchar_t ))); + buffer, static_cast( read + new_read ), wbuffer, static_cast( 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 )) { @@ -1596,7 +1600,8 @@ void core_get_field_common( __inout sqlsrv_stmt* stmt, SQLUSMALLINT field_index, { zval_auto_ptr return_value_z; - return_value_z = (zval *)sqlsrv_malloc(sizeof(zval*)); + return_value_z = (zval *)sqlsrv_malloc(sizeof(zval)); + ZVAL_UNDEF(return_value_z); php_stream* stream = NULL; sqlsrv_stream* ss = NULL; SQLLEN sql_type; @@ -1627,7 +1632,7 @@ void core_get_field_common( __inout sqlsrv_stmt* stmt, SQLUSMALLINT field_index, // mark this as our active stream stmt->active_stream = return_value_z; - *field_value = reinterpret_cast( return_value_z.get() ); + *field_value = reinterpret_cast( return_value_z.get() ); return_value_z.transferred(); break; } @@ -2410,9 +2415,9 @@ void resize_output_buffer_if_necessary( sqlsrv_stmt* stmt, zval* param_z, SQLULE void save_output_param_for_later( sqlsrv_stmt* stmt, sqlsrv_output_param& param TSRMLS_DC ) { HashTable* param_ht = Z_ARRVAL( stmt->output_params ); - zend_ulong paramno = static_cast(param.param_num); - core::sqlsrv_zend_hash_index_update_mem(*stmt, param_ht, paramno, ¶m, sizeof(sqlsrv_output_param)); - Z_TRY_ADDREF_P(param.param_z); // we have a reference to the param + zend_ulong paramno = static_cast( param.param_num ); + core::sqlsrv_zend_hash_index_update_mem(*stmt, param_ht, paramno, ¶m, sizeof( sqlsrv_output_param )); + Z_TRY_ADDREF_P( param.param_z ); // we have a reference to the param } @@ -2427,14 +2432,14 @@ 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( zval* data ) { - sqlsrv_output_param *output_param = reinterpret_cast( Z_PTR_P(data) ); + sqlsrv_output_param *output_param = static_cast( Z_PTR_P( data )); zval_ptr_dtor( output_param->param_z ); // undo the reference to the string we will no longer hold } // called by Zend for each stream in the sqlsrv_stmt::param_streams hash table when it is cleaned/destroyed -void sqlsrv_stream_dtor(zval* data ) +void sqlsrv_stream_dtor( zval* data ) { - sqlsrv_stream* stream_encoding = reinterpret_cast( Z_PTR_P(data) ); + sqlsrv_stream* stream_encoding = static_cast( Z_PTR_P( data )); zval_ptr_dtor( stream_encoding->stream_z ); // undo the reference to the stream we will no longer hold } diff --git a/pdo_sqlsrv/core_stream.cpp b/pdo_sqlsrv/core_stream.cpp index d2cb386e..17468d8c 100644 --- a/pdo_sqlsrv/core_stream.cpp +++ b/pdo_sqlsrv/core_stream.cpp @@ -165,7 +165,7 @@ size_t sqlsrv_stream_read( php_stream* stream, __out_bcount(count) char* buf, si } int enc_len = WideCharToMultiByte( ss->encoding, flags, reinterpret_cast( temp_buf.get() ), - int(read >> 1), buf, int(count), NULL, NULL ); + static_cast(read >> 1), buf, static_cast(count), NULL, NULL ); if( enc_len == 0 ) { diff --git a/pdo_sqlsrv/pdo_stmt.cpp b/pdo_sqlsrv/pdo_stmt.cpp index 20a35936..74aaa992 100644 --- a/pdo_sqlsrv/pdo_stmt.cpp +++ b/pdo_sqlsrv/pdo_stmt.cpp @@ -133,7 +133,7 @@ void set_stmt_cursors( sqlsrv_stmt* stmt, zval* value_z TSRMLS_DC ) } zend_long pdo_cursor_type = Z_LVAL_P( value_z ); - SQLULEN odbc_cursor_type = -1; + 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 ); } - SQLULEN odbc_cursor_type = Z_LVAL_P( value_z ); + long odbc_cursor_type = static_cast( Z_LVAL_P( value_z ) ); core_sqlsrv_set_scrollable( stmt, odbc_cursor_type TSRMLS_CC ); diff --git a/sample/pdo_sqlsrv_sample.php b/sample/pdo_sqlsrv_sample.php new file mode 100644 index 00000000..3c92efa3 --- /dev/null +++ b/sample/pdo_sqlsrv_sample.php @@ -0,0 +1,74 @@ +query( $tsql ); + + //Error handling + FormatErrors ($conn->errorInfo()); + + $productCount = 0; + $ctr = 0; + ?> + +

First 10 results are :

+ + fetch(PDO::FETCH_ASSOC)) + { + if($ctr>9) + break; + $ctr++; + echo($row['CompanyName']); + echo("
"); + $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()); + ?> + +

Product Key inserted is :

+ + fetch(PDO::FETCH_ASSOC)) + { + echo($row['ProductID']."
"); + } + $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:
"; + + echo "SQLSTATE: ".$error[0]."
"; + echo "Code: ".$error[1]."
"; + echo "Message: ".$error[2]."
"; + } +?> \ No newline at end of file diff --git a/sample/sqlsrv_sample.php b/sample/sqlsrv_sample.php index c9e262a7..886e06da 100644 --- a/sample/sqlsrv_sample.php +++ b/sample/sqlsrv_sample.php @@ -1,7 +1,7 @@ "yourpassword", "Uid"=>"yourusername", "PWD"=>"yourpassword"); + $connectionOptions = array("Database"=>"yourdatabase", "Uid"=>"yourusername", "PWD"=>"yourpassword"); //Establishes the connection $conn = sqlsrv_connect($serverName, $connectionOptions); @@ -28,7 +28,7 @@ } sqlsrv_free_stmt($getProducts); - $tsql = "INSERT SalesLT.Product (Name, ProductNumber, StandardCost, ListPrice, SellStartDate) OUTPUT INSERTED.ProductID VALUES ('SQL New 1', 'SQL New 2', 0, 0, getdate())"; + $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) diff --git a/sqlsrv/conn.cpp b/sqlsrv/conn.cpp index 0c1dab6f..998d50b3 100644 --- a/sqlsrv/conn.cpp +++ b/sqlsrv/conn.cpp @@ -571,7 +571,7 @@ PHP_FUNCTION( sqlsrv_close ) // 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 ) { // Check if it was a zval @@ -580,7 +580,7 @@ PHP_FUNCTION( sqlsrv_close ) 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; } @@ -588,9 +588,15 @@ PHP_FUNCTION( sqlsrv_close ) THROW_CORE_ERROR( error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ); } } - + conn = static_cast( zend_fetch_resource( Z_RES_P( conn_r ) TSRMLS_CC, ss_sqlsrv_conn::resource_name, ss_sqlsrv_conn::descriptor )); - CHECK_CUSTOM_ERROR(( conn == NULL ), error_ctx, SS_SQLSRV_ERROR_INVALID_FUNCTION_PARAMETER, _FN_ ) { + + // 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(); } @@ -603,8 +609,8 @@ PHP_FUNCTION( sqlsrv_close ) 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; } diff --git a/sqlsrv/core_conn.cpp b/sqlsrv/core_conn.cpp index 89f213b4..825393e7 100644 --- a/sqlsrv/core_conn.cpp +++ b/sqlsrv/core_conn.cpp @@ -130,9 +130,9 @@ sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_ // We only support UTF-8 encoding for connection string. // Convert our UTF-8 connection string to UTF-16 before connecting with SQLDriverConnnectW - wconn_len = (unsigned int) (conn_str.length() + 1) * sizeof( wchar_t ); + wconn_len = static_cast( conn_str.length() + 1 ) * sizeof( wchar_t ); - wconn_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_UTF8, conn_str.c_str(), (unsigned int) conn_str.length(), &wconn_len ); + wconn_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_UTF8, conn_str.c_str(), static_cast( conn_str.length() ), &wconn_len ); CHECK_CUSTOM_ERROR( wconn_string == NULL, conn, SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE, get_last_error_message() ) { throw core::CoreException(); diff --git a/sqlsrv/core_sqlsrv.h b/sqlsrv/core_sqlsrv.h index 6858b1c9..2563ee70 100644 --- a/sqlsrv/core_sqlsrv.h +++ b/sqlsrv/core_sqlsrv.h @@ -1318,7 +1318,7 @@ void core_sqlsrv_get_field(sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_p bool core_sqlsrv_has_any_result( sqlsrv_stmt* stmt TSRMLS_DC ); void core_sqlsrv_next_result( sqlsrv_stmt* stmt TSRMLS_DC, bool finalize_output_params = true, bool throw_on_errors = true ); void core_sqlsrv_post_param( sqlsrv_stmt* stmt, zend_ulong paramno, zval* param_z TSRMLS_DC ); -void core_sqlsrv_set_scrollable( sqlsrv_stmt* stmt, SQLULEN cursor_type 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 ); @@ -2061,6 +2061,9 @@ 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) diff --git a/sqlsrv/core_stmt.cpp b/sqlsrv/core_stmt.cpp index 16f0a950..e727262f 100644 --- a/sqlsrv/core_stmt.cpp +++ b/sqlsrv/core_stmt.cpp @@ -348,8 +348,8 @@ void core_sqlsrv_bind_param(sqlsrv_stmt* stmt, SQLUSMALLINT param_num, SQLSMALLI 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); + 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. @@ -422,7 +422,7 @@ void core_sqlsrv_bind_param(sqlsrv_stmt* stmt, SQLUSMALLINT param_num, SQLSMALLI // 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, (unsigned int)param_num, param_z, encoding, column_size, decimal_digits TSRMLS_CC ); + default_sql_size_and_scale( stmt, static_cast( param_num ), param_z, encoding, column_size, decimal_digits TSRMLS_CC ); } // determine the ODBC C type @@ -442,13 +442,15 @@ void core_sqlsrv_bind_param(sqlsrv_stmt* stmt, SQLUSMALLINT param_num, SQLSMALLI 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 = ¶m_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_ref, int(param_num), zval_was_bool ); + sqlsrv_output_param output_param( param_ref, static_cast( param_num ), zval_was_bool ); save_output_param_for_later( stmt, output_param TSRMLS_CC ); } } @@ -456,11 +458,11 @@ void core_sqlsrv_bind_param(sqlsrv_stmt* stmt, SQLUSMALLINT param_num, SQLSMALLI case IS_DOUBLE: { buffer = ¶m_z->value; - buffer_len = sizeof(Z_DVAL_P(param_z)); + 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_ref, int(param_num), false ); + sqlsrv_output_param output_param(param_ref, static_cast( param_num ), false ); save_output_param_for_later( stmt, output_param TSRMLS_CC ); } } @@ -472,7 +474,7 @@ void core_sqlsrv_bind_param(sqlsrv_stmt* stmt, SQLUSMALLINT param_num, SQLSMALLI if( direction == SQL_PARAM_INPUT && encoding == CP_UTF8 ) { zval wbuffer_z; - ZVAL_NULL(&wbuffer_z); + ZVAL_NULL( &wbuffer_z ); bool converted = convert_input_param_to_utf16( param_z, &wbuffer_z ); CHECK_CUSTOM_ERROR( !converted, stmt, SQLSRV_ERROR_INPUT_PARAM_ENCODING_TRANSLATE, @@ -519,12 +521,12 @@ void core_sqlsrv_bind_param(sqlsrv_stmt* stmt, SQLUSMALLINT param_num, SQLSMALLI 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_ref, encoding, param_num, (SQLUINTEGER)buffer_len ); + sqlsrv_output_param output_param( param_ref, encoding, param_num, static_cast( 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 @@ -552,7 +554,7 @@ void core_sqlsrv_bind_param(sqlsrv_stmt* stmt, SQLUSMALLINT param_num, SQLSMALLI 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( param_num ); - Z_TRY_ADDREF_P(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; } @@ -702,6 +704,8 @@ void core_sqlsrv_execute( sqlsrv_stmt* stmt TSRMLS_DC, const char* sql, int sql_ finalize_output_parameters( stmt TSRMLS_CC ); } + // stream parameters are sent, clean the Hashtable + zend_hash_clean( Z_ARRVAL( stmt->param_streams )); } catch( core::CoreException& e ) { @@ -935,7 +939,7 @@ void core_sqlsrv_get_field( sqlsrv_stmt* stmt, SQLUSMALLINT field_index, sqlsrv_ 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( (SQLINTEGER)sql_field_type, (SQLUINTEGER)sql_field_len, prefer_string ); + sqlsrv_php_type = stmt->sql_type_to_php_type( static_cast( sql_field_type ), static_cast( sql_field_len ), prefer_string ); } // Verify that we have an acceptable type to convert. @@ -1060,7 +1064,7 @@ void core_sqlsrv_post_param( sqlsrv_stmt* stmt, zend_ulong param_num, zval* para } //Calls SQLSetStmtAttr to set a cursor. -void core_sqlsrv_set_scrollable( sqlsrv_stmt* stmt, SQLULEN cursor_type TSRMLS_DC ) +void core_sqlsrv_set_scrollable( sqlsrv_stmt* stmt, unsigned long cursor_type TSRMLS_DC ) { try { @@ -1139,7 +1143,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, (long)Z_LVAL_P( value_z ) TSRMLS_CC ); + core_sqlsrv_set_query_timeout( stmt, static_cast( Z_LVAL_P( value_z )) TSRMLS_CC ); } catch( core::CoreException& ) { throw; @@ -1242,7 +1246,7 @@ bool core_sqlsrv_send_stream_packet( sqlsrv_stmt* stmt TSRMLS_DC ) throw core::CoreException(); } - stmt->current_stream_read += (unsigned int)read; + stmt->current_stream_read += static_cast( 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 @@ -1256,7 +1260,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, int(read), wbuffer, int(sizeof( wbuffer ) / sizeof( wchar_t ))); + buffer, static_cast( read ), wbuffer, static_cast( 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 @@ -1272,7 +1276,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, int(read + new_read), wbuffer, int(sizeof( wbuffer ) / sizeof( wchar_t ))); + buffer, static_cast( read + new_read ), wbuffer, static_cast( 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 )) { @@ -1596,7 +1600,8 @@ void core_get_field_common( __inout sqlsrv_stmt* stmt, SQLUSMALLINT field_index, { zval_auto_ptr return_value_z; - return_value_z = (zval *)sqlsrv_malloc(sizeof(zval*)); + return_value_z = (zval *)sqlsrv_malloc(sizeof(zval)); + ZVAL_UNDEF(return_value_z); php_stream* stream = NULL; sqlsrv_stream* ss = NULL; SQLLEN sql_type; @@ -1627,7 +1632,7 @@ void core_get_field_common( __inout sqlsrv_stmt* stmt, SQLUSMALLINT field_index, // mark this as our active stream stmt->active_stream = return_value_z; - *field_value = reinterpret_cast( return_value_z.get() ); + *field_value = reinterpret_cast( return_value_z.get() ); return_value_z.transferred(); break; } @@ -2410,9 +2415,9 @@ void resize_output_buffer_if_necessary( sqlsrv_stmt* stmt, zval* param_z, SQLULE void save_output_param_for_later( sqlsrv_stmt* stmt, sqlsrv_output_param& param TSRMLS_DC ) { HashTable* param_ht = Z_ARRVAL( stmt->output_params ); - zend_ulong paramno = static_cast(param.param_num); - core::sqlsrv_zend_hash_index_update_mem(*stmt, param_ht, paramno, ¶m, sizeof(sqlsrv_output_param)); - Z_TRY_ADDREF_P(param.param_z); // we have a reference to the param + zend_ulong paramno = static_cast( param.param_num ); + core::sqlsrv_zend_hash_index_update_mem(*stmt, param_ht, paramno, ¶m, sizeof( sqlsrv_output_param )); + Z_TRY_ADDREF_P( param.param_z ); // we have a reference to the param } @@ -2427,14 +2432,14 @@ 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( zval* data ) { - sqlsrv_output_param *output_param = reinterpret_cast( Z_PTR_P(data) ); + sqlsrv_output_param *output_param = static_cast( Z_PTR_P( data )); zval_ptr_dtor( output_param->param_z ); // undo the reference to the string we will no longer hold } // called by Zend for each stream in the sqlsrv_stmt::param_streams hash table when it is cleaned/destroyed -void sqlsrv_stream_dtor(zval* data ) +void sqlsrv_stream_dtor( zval* data ) { - sqlsrv_stream* stream_encoding = reinterpret_cast( Z_PTR_P(data) ); + sqlsrv_stream* stream_encoding = static_cast( Z_PTR_P( data )); zval_ptr_dtor( stream_encoding->stream_z ); // undo the reference to the stream we will no longer hold } diff --git a/sqlsrv/core_stream.cpp b/sqlsrv/core_stream.cpp index d2cb386e..17468d8c 100644 --- a/sqlsrv/core_stream.cpp +++ b/sqlsrv/core_stream.cpp @@ -165,7 +165,7 @@ size_t sqlsrv_stream_read( php_stream* stream, __out_bcount(count) char* buf, si } int enc_len = WideCharToMultiByte( ss->encoding, flags, reinterpret_cast( temp_buf.get() ), - int(read >> 1), buf, int(count), NULL, NULL ); + static_cast(read >> 1), buf, static_cast(count), NULL, NULL ); if( enc_len == 0 ) { diff --git a/sqlsrv/stmt.cpp b/sqlsrv/stmt.cpp index 1bdf5864..9d482172 100644 --- a/sqlsrv/stmt.cpp +++ b/sqlsrv/stmt.cpp @@ -1059,7 +1059,6 @@ PHP_FUNCTION( sqlsrv_get_field ) // get the statement, the field index and the optional type PROCESS_PARAMS( stmt, "rl|l", _FN_, 2, &field_index, &sqlsrv_php_type ); - try { // validate that the field index is within range @@ -1259,7 +1258,6 @@ void bind_params( ss_sqlsrv_stmt* stmt TSRMLS_DC ) 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(); } @@ -1406,19 +1404,24 @@ PHP_FUNCTION( sqlsrv_free_stmt ) // verify the resource so we know we're deleting a statement stmt = static_cast(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 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; } @@ -1440,7 +1443,7 @@ void stmt_option_scrollable:: operator()( sqlsrv_stmt* stmt, stmt_option const* } const char* scroll_type = Z_STRVAL_P( value_z ); - SQLULEN 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 )) { @@ -1518,7 +1521,10 @@ zval* convert_to_zval( SQLSRV_PHPTYPE sqlsrv_php_type, void* in_val, SQLLEN fiel case SQLSRV_PHPTYPE_STREAM: case SQLSRV_PHPTYPE_DATETIME : { - out_zval = (reinterpret_cast( in_val )); + out_zval = ( static_cast( in_val ) ); + + //addref here because deleting out_zval later will decrement the refcount + Z_TRY_ADDREF_P( out_zval ); in_val = NULL; break; }