From 01c9f22f63fa8e7b047c16136b267890fc76ba42 Mon Sep 17 00:00:00 2001 From: v-kaywon Date: Tue, 5 Sep 2017 16:51:40 -0700 Subject: [PATCH 1/7] add code for AE insertion --- source/shared/core_results.cpp | 4 +-- source/shared/core_sqlsrv.h | 31 +++++++++++++++--- source/shared/core_stmt.cpp | 59 ++++++++++++++++++++++++++-------- source/shared/core_util.cpp | 10 +++--- 4 files changed, 80 insertions(+), 24 deletions(-) diff --git a/source/shared/core_results.cpp b/source/shared/core_results.cpp index 5558e3fd..cbc74592 100644 --- a/source/shared/core_results.cpp +++ b/source/shared/core_results.cpp @@ -342,12 +342,12 @@ struct row_dtor_closure { sqlsrv_error* odbc_get_diag_rec( _In_ sqlsrv_stmt* odbc, _In_ SQLSMALLINT record_number ) { SQLWCHAR wsql_state[ SQL_SQLSTATE_BUFSIZE ]; - SQLWCHAR wnative_message[ SQL_MAX_MESSAGE_LENGTH + 1 ]; + SQLWCHAR wnative_message[ SQL_MAX_ERROR_MESSAGE_LENGTH + 1 ]; SQLINTEGER native_code; SQLSMALLINT wnative_message_len = 0; SQLRETURN r = SQLGetDiagRecW( SQL_HANDLE_STMT, odbc->handle(), record_number, wsql_state, &native_code, wnative_message, - SQL_MAX_MESSAGE_LENGTH + 1, &wnative_message_len ); + SQL_MAX_ERROR_MESSAGE_LENGTH + 1, &wnative_message_len ); if( !SQL_SUCCEEDED( r ) || r == SQL_NO_DATA ) { return NULL; } diff --git a/source/shared/core_sqlsrv.h b/source/shared/core_sqlsrv.h index ff5e1024..58af8895 100644 --- a/source/shared/core_sqlsrv.h +++ b/source/shared/core_sqlsrv.h @@ -172,6 +172,7 @@ const int SQL_SERVER_MAX_FIELD_SIZE = 8000; const int SQL_SERVER_MAX_PRECISION = 38; const int SQL_SERVER_MAX_TYPE_SIZE = 0; const int SQL_SERVER_MAX_PARAMS = 2100; +const int SQL_MAX_ERROR_MESSAGE_LENGTH = SQL_MAX_MESSAGE_LENGTH * 4; // max size of a date time string when converting from a DateTime object to a string const int MAX_DATETIME_STRING_LEN = 256; @@ -772,9 +773,9 @@ struct sqlsrv_error : public sqlsrv_error_const { sqlsrv_error( _In_ SQLCHAR* sql_state, _In_ SQLCHAR* message, _In_ SQLINTEGER code, _In_ bool printf_format = false ) { sqlstate = reinterpret_cast( sqlsrv_malloc( SQL_SQLSTATE_BUFSIZE )); - native_message = reinterpret_cast( sqlsrv_malloc( SQL_MAX_MESSAGE_LENGTH + 1 )); + native_message = reinterpret_cast( sqlsrv_malloc( SQL_MAX_ERROR_MESSAGE_LENGTH + 1 )); strcpy_s( reinterpret_cast( sqlstate ), SQL_SQLSTATE_BUFSIZE, reinterpret_cast( sql_state )); - strcpy_s( reinterpret_cast( native_message ), SQL_MAX_MESSAGE_LENGTH + 1, reinterpret_cast( message )); + strcpy_s( reinterpret_cast( native_message ), SQL_MAX_ERROR_MESSAGE_LENGTH + 1, reinterpret_cast( message )); native_code = code; format = printf_format; } @@ -1835,11 +1836,11 @@ namespace core { // and return a more helpful message prepended to the ODBC errors if that error occurs if( !SQL_SUCCEEDED( r )) { - SQLCHAR err_msg[ SQL_MAX_MESSAGE_LENGTH + 1 ] = { '\0' }; + SQLCHAR err_msg[ SQL_MAX_ERROR_MESSAGE_LENGTH + 1 ] = { '\0' }; SQLSMALLINT len = 0; SQLRETURN rtemp = ::SQLGetDiagField( stmt->handle_type(), stmt->handle(), 1, SQL_DIAG_MESSAGE_TEXT, - err_msg, SQL_MAX_MESSAGE_LENGTH, &len ); + err_msg, SQL_MAX_ERROR_MESSAGE_LENGTH, &len ); CHECK_SQL_ERROR_OR_WARNING( rtemp, stmt ) { @@ -1965,6 +1966,17 @@ namespace core { } } + inline void SQLDescribeParam( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT paramno, _Out_opt_ SQLSMALLINT* data_type, _Out_opt_ SQLULEN* col_size, + _Out_opt_ SQLSMALLINT* decimal_digits, _Out_opt_ SQLSMALLINT* nullable TSRMLS_DC ) + { + SQLRETURN r; + r = ::SQLDescribeParam( stmt->handle(), paramno, data_type, col_size, decimal_digits, nullable ); + + CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { + throw CoreException(); + } + } + inline void SQLEndTran( _In_ SQLSMALLINT handleType, _Inout_ sqlsrv_conn* conn, _In_ SQLSMALLINT completionType TSRMLS_DC ) { SQLRETURN r = ::SQLEndTran( handleType, conn->handle(), completionType ); @@ -2184,6 +2196,17 @@ namespace core { } } + inline void SQLSetDescField(_Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT rec_num, _In_ SQLSMALLINT fld_id, _In_reads_bytes_opt_(str_len) SQLPOINTER value_ptr, _In_ SQLINTEGER str_len TSRMLS_DC) + { + SQLRETURN r; + SQLHDESC hIpd = NULL; + core::SQLGetStmtAttr(stmt, SQL_ATTR_IMP_PARAM_DESC, &hIpd, 0, 0); + r = ::SQLSetDescField(hIpd, rec_num, fld_id, value_ptr, str_len); + + CHECK_SQL_ERROR_OR_WARNING(r, stmt) { + throw CoreException(); + } + } inline void SQLSetEnvAttr( _Inout_ sqlsrv_context& ctx, _In_ SQLINTEGER attr, _In_reads_bytes_opt_(str_len) SQLPOINTER value_ptr, _In_ SQLINTEGER str_len TSRMLS_DC ) { diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index 5e3883de..26a61f43 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -101,6 +101,8 @@ void default_sql_size_and_scale( _Inout_ sqlsrv_stmt* stmt, _In_opt_ unsigned in // given a zval and encoding, determine the appropriate sql type, column size, and decimal scale (if appropriate) void default_sql_type( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno, _In_ zval* param_z, _In_ SQLSRV_ENCODING encoding, _Out_ SQLSMALLINT& sql_type TSRMLS_DC ); +void ae_get_sql_type_info( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno, _In_ SQLSMALLINT direction, _In_ zval* param_z, _In_ SQLSRV_ENCODING encoding, + _Out_ SQLSMALLINT& sql_type, _Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits TSRMLS_DC ); void col_cache_dtor( _Inout_ zval* data_z ); void field_cache_dtor( _Inout_ zval* data_z ); void finalize_output_parameters( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC ); @@ -110,8 +112,8 @@ stmt_option const* get_stmt_option( sqlsrv_conn const* conn, _In_ zend_ulong key bool is_valid_sqlsrv_phptype( _In_ sqlsrv_phptype type ); // assure there is enough space for the output parameter string void resize_output_buffer_if_necessary( _Inout_ sqlsrv_stmt* stmt, _Inout_ zval* param_z, _In_ SQLULEN paramno, SQLSRV_ENCODING encoding, - _In_ SQLSMALLINT c_type, _In_ SQLSMALLINT sql_type, _In_ SQLULEN column_size, _Out_writes_(buffer_len) SQLPOINTER& buffer, - _Out_ SQLLEN& buffer_len TSRMLS_DC ); + _In_ SQLSMALLINT c_type, _In_ SQLSMALLINT sql_type, _In_ SQLULEN column_size, _In_ SQLSMALLINT decimal_digits, + _Out_writes_(buffer_len) SQLPOINTER& buffer, _Out_ SQLLEN& buffer_len TSRMLS_DC ); bool reset_ae_stream_cursor( _Inout_ sqlsrv_stmt* stmt ); void save_output_param_for_later( _Inout_ sqlsrv_stmt* stmt, _Inout_ sqlsrv_output_param& param TSRMLS_DC ); // send all the stream data @@ -436,16 +438,21 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_ ( encoding == SQLSRV_ENCODING_SYSTEM || encoding == SQLSRV_ENCODING_UTF8 || encoding == SQLSRV_ENCODING_BINARY ), "core_sqlsrv_bind_param: invalid encoding" ); - // 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 ); + if ( stmt->conn->ce_option.enabled && ( sql_type == SQL_UNKNOWN_TYPE || column_size == SQLSRV_UNKNOWN_SIZE ) && + Z_TYPE_P( param_z ) != IS_NULL && Z_TYPE_P( param_z ) != IS_FALSE && Z_TYPE_P( param_z) != IS_TRUE ) { + ae_get_sql_type_info( stmt, param_num, direction, param_z, encoding, sql_type, column_size, decimal_digits TSRMLS_CC ); } + else { + // 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 ); + } - // 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, static_cast( param_num ), param_z, encoding, column_size, decimal_digits 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, static_cast(param_num), param_z, encoding, column_size, decimal_digits TSRMLS_CC ); + } } - // determine the ODBC C type c_type = default_c_type( stmt, param_num, param_z, encoding TSRMLS_CC ); @@ -538,7 +545,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_ // since this is an output string, assure there is enough space to hold the requested size and // set all the variables necessary (param_z, buffer, buffer_len, and ind_ptr) - resize_output_buffer_if_necessary( stmt, param_z, param_num, encoding, c_type, sql_type, column_size, + resize_output_buffer_if_necessary( stmt, param_z, param_num, encoding, c_type, sql_type, column_size, decimal_digits, buffer, buffer_len TSRMLS_CC ); // save the parameter to be adjusted and/or converted after the results are processed @@ -552,7 +559,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_ // 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 // output parameters. - if( direction == SQL_PARAM_OUTPUT ) { + if( direction == SQL_PARAM_OUTPUT && !stmt->conn->ce_option.enabled ) { switch( sql_type ) { @@ -663,6 +670,13 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_ core::SQLBindParameter( stmt, param_num + 1, direction, c_type, sql_type, column_size, decimal_digits, buffer, buffer_len, &ind_ptr TSRMLS_CC ); + if ( stmt->conn->ce_option.enabled && sql_type == SQL_TYPE_TIMESTAMP ) + { + if ( decimal_digits == 3 ) + core::SQLSetDescField( stmt, param_num + 1, SQL_CA_SS_SERVER_TYPE, (SQLPOINTER)SQL_SS_TYPE_DATETIME, 0 ); + else if (decimal_digits == 0) + core::SQLSetDescField( stmt, param_num + 1, SQL_CA_SS_SERVER_TYPE, (SQLPOINTER)SQL_SS_TYPE_SMALLDATETIME, 0 ); + } } catch( core::CoreException& e ) { stmt->free_param_data( TSRMLS_C ); @@ -2004,6 +2018,19 @@ void default_sql_size_and_scale( _Inout_ sqlsrv_stmt* stmt, _In_opt_ unsigned in } } +void ae_get_sql_type_info( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno, _In_ SQLSMALLINT direction, _In_ zval* param_z, _In_ SQLSRV_ENCODING encoding, + _Out_ SQLSMALLINT& sql_type, _Out_ SQLULEN& column_size, _Out_ SQLSMALLINT& decimal_digits TSRMLS_DC ) +{ + SQLSMALLINT Nullable; + core::SQLDescribeParam( stmt, paramno + 1, &sql_type, &column_size, &decimal_digits, &Nullable ); + // without AE, binding an INPUT bigint with sql type SQL_WVARCHAR returns a conversion error between nvarchar and bigint + // similate without AE by setting the sql type back to SQL_WVARCHAR using default_sql_type + if ( sql_type == SQL_BIGINT && Z_TYPE_P(param_z) == IS_STRING && direction == SQL_PARAM_INPUT ) { + default_sql_type( stmt, paramno, param_z, encoding, sql_type TSRMLS_CC ); + default_sql_size_and_scale( stmt, paramno, param_z, encoding, column_size, decimal_digits ); + } +} + void col_cache_dtor( _Inout_ zval* data_z ) { col_cache* cache = static_cast( Z_PTR_P( data_z )); @@ -2485,8 +2512,8 @@ bool is_valid_sqlsrv_phptype( _In_ sqlsrv_phptype type ) // stmt->param_ind_ptrs are modified to hold the correct values for SQLBindParameter void resize_output_buffer_if_necessary( _Inout_ sqlsrv_stmt* stmt, _Inout_ zval* param_z, _In_ SQLULEN paramno, SQLSRV_ENCODING encoding, - _In_ SQLSMALLINT c_type, _In_ SQLSMALLINT sql_type, _In_ SQLULEN column_size, _Out_writes_(buffer_len) SQLPOINTER& buffer, - _Out_ SQLLEN& buffer_len TSRMLS_DC ) + _In_ SQLSMALLINT c_type, _In_ SQLSMALLINT sql_type, _In_ SQLULEN column_size, _In_ SQLSMALLINT decimal_digits, + _Out_writes_(buffer_len) SQLPOINTER& buffer, _Out_ 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 ); @@ -2501,6 +2528,12 @@ void resize_output_buffer_if_necessary( _Inout_ sqlsrv_stmt* stmt, _Inout_ zval* // account for the NULL terminator returned by ODBC and needed by Zend to avoid a "String not null terminated" debug warning SQLULEN field_size = column_size; + // with AE on, when column_size is retrieved from SQLDescribeParam, column_size does not include the decimal place + // include the decimal for output params by adding elem_size + if ( stmt->conn->ce_option.enabled && decimal_digits != 0 ) + { + field_size += elem_size; + } if (column_size == SQL_SS_LENGTH_UNLIMITED) { field_size = SQL_SERVER_MAX_FIELD_SIZE / elem_size; } diff --git a/source/shared/core_util.cpp b/source/shared/core_util.cpp index da64d42a..5230a8a2 100644 --- a/source/shared/core_util.cpp +++ b/source/shared/core_util.cpp @@ -220,7 +220,7 @@ bool core_sqlsrv_get_odbc_error( _Inout_ sqlsrv_context& ctx, _In_ int record_nu SQLRETURN r = SQL_SUCCESS; SQLSMALLINT wmessage_len = 0; SQLWCHAR wsqlstate[ SQL_SQLSTATE_BUFSIZE ] = { L'\0' }; - SQLWCHAR wnative_message[ SQL_MAX_MESSAGE_LENGTH + 1 ] = { L'\0' }; + SQLWCHAR wnative_message[ ( SQL_MAX_ERROR_MESSAGE_LENGTH + 1 ) * 2 ] = { L'\0' }; SQLSRV_ENCODING enc = ctx.encoding(); switch( h_type ) { @@ -245,7 +245,7 @@ bool core_sqlsrv_get_odbc_error( _Inout_ sqlsrv_context& ctx, _In_ int record_nu default: error = new ( sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error(); r = SQLGetDiagRecW( h_type, h, record_number, wsqlstate, &error->native_code, wnative_message, - SQL_MAX_MESSAGE_LENGTH + 1, &wmessage_len ); + (SQL_MAX_ERROR_MESSAGE_LENGTH + 1)*2, &wmessage_len ); // don't use the CHECK* macros here since it will trigger reentry into the error handling system // Workaround for a bug in unixODBC 2.3.4 when connection pooling is enabled (PDO SQLSRV). // Instead of returning false, we return an empty error message to prevent the driver from throwing an exception. @@ -290,12 +290,12 @@ void core_sqlsrv_format_driver_error( _In_ sqlsrv_context& ctx, _In_ sqlsrv_erro // allocate space for the formatted message formatted_error = new (sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error(); formatted_error->sqlstate = reinterpret_cast( sqlsrv_malloc( SQL_SQLSTATE_BUFSIZE )); - formatted_error->native_message = reinterpret_cast( sqlsrv_malloc( SQL_MAX_MESSAGE_LENGTH + 1 )); + formatted_error->native_message = reinterpret_cast( sqlsrv_malloc( SQL_MAX_ERROR_MESSAGE_LENGTH + 1 )); DWORD rc = FormatMessage( FORMAT_MESSAGE_FROM_STRING, reinterpret_cast( custom_error->native_message ), 0, 0, - reinterpret_cast( formatted_error->native_message ), SQL_MAX_MESSAGE_LENGTH, args ); + reinterpret_cast( formatted_error->native_message ), SQL_MAX_ERROR_MESSAGE_LENGTH, args ); if( rc == 0 ) { - strcpy_s( reinterpret_cast( formatted_error->native_message ), SQL_MAX_MESSAGE_LENGTH, + strcpy_s( reinterpret_cast( formatted_error->native_message ), SQL_MAX_ERROR_MESSAGE_LENGTH, reinterpret_cast( INTERNAL_FORMAT_ERROR )); } From cdc60e7018408cd8759134d72b64c330946d6971 Mon Sep 17 00:00:00 2001 From: v-kaywon Date: Wed, 6 Sep 2017 12:58:09 -0700 Subject: [PATCH 2/7] change test to provide sql type when binding parameter query using sqlsrv_prepare in a column encryption enabled connection --- test/functional/sqlsrv/sqlsrv_encrypted_patients_ksp.phpt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/functional/sqlsrv/sqlsrv_encrypted_patients_ksp.phpt b/test/functional/sqlsrv/sqlsrv_encrypted_patients_ksp.phpt index d7d0f403..d91a58cf 100644 --- a/test/functional/sqlsrv/sqlsrv_encrypted_patients_ksp.phpt +++ b/test/functional/sqlsrv/sqlsrv_encrypted_patients_ksp.phpt @@ -167,7 +167,8 @@ Test simple insert, fetch and update with ColumnEncryption enabled and a custome sqlsrv_free_stmt($stmt); $callResult = '1900-01-01'; - $params = array( array( 1, SQLSRV_PARAM_IN ), array( &$callResult, SQLSRV_PARAM_OUT, null, SQLSRV_SQLTYPE_DATE)); + //when binding parameter using sqlsrv_query in a column encryption enabled connection, need to provide the sql_type in all parameters + $params = array( array( 1, SQLSRV_PARAM_IN, null, SQLSRV_SQLTYPE_INT ), array( &$callResult, SQLSRV_PARAM_OUT, null, SQLSRV_SQLTYPE_DATE)); $callArgs = "?, ?"; $stmt = sqlsrv_query($conn, "{ CALL [$procName] ($callArgs)}", $params); if (! $stmt ) @@ -187,7 +188,8 @@ Test simple insert, fetch and update with ColumnEncryption enabled and a custome sqlsrv_free_stmt($stmt); $callResult = '000-00-0000'; - $params = array( array( 1, SQLSRV_PARAM_IN ), array( &$callResult, SQLSRV_PARAM_OUT, null, SQLSRV_SQLTYPE_CHAR(11))); + //when binding parameter using sqlsrv_query in a column encryption enabled connection, need to provide the sql_type in all parameters + $params = array( array( 1, SQLSRV_PARAM_IN, null, SQLSRV_SQLTYPE_INT ), array( &$callResult, SQLSRV_PARAM_OUT, null, SQLSRV_SQLTYPE_CHAR(11))); $callArgs = "?, ?"; $stmt = sqlsrv_query($conn, "{ CALL [$procName] ($callArgs)}", $params); if (! $stmt ) From 376461fab491f370e3d80a933259cacbb252bdc1 Mon Sep 17 00:00:00 2001 From: v-kaywon Date: Thu, 7 Sep 2017 15:26:46 -0700 Subject: [PATCH 3/7] fix according to code review --- source/shared/core_sqlsrv.h | 9 +++++---- source/shared/core_stmt.cpp | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/source/shared/core_sqlsrv.h b/source/shared/core_sqlsrv.h index 58af8895..cb655909 100644 --- a/source/shared/core_sqlsrv.h +++ b/source/shared/core_sqlsrv.h @@ -172,6 +172,7 @@ const int SQL_SERVER_MAX_FIELD_SIZE = 8000; const int SQL_SERVER_MAX_PRECISION = 38; const int SQL_SERVER_MAX_TYPE_SIZE = 0; const int SQL_SERVER_MAX_PARAMS = 2100; +// increase the maximum message length to accommodate for the long error returned for operand type clash const int SQL_MAX_ERROR_MESSAGE_LENGTH = SQL_MAX_MESSAGE_LENGTH * 4; // max size of a date time string when converting from a DateTime object to a string @@ -2196,14 +2197,14 @@ namespace core { } } - inline void SQLSetDescField(_Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT rec_num, _In_ SQLSMALLINT fld_id, _In_reads_bytes_opt_(str_len) SQLPOINTER value_ptr, _In_ SQLINTEGER str_len TSRMLS_DC) + inline void SQLSetDescField( _Inout_ sqlsrv_stmt* stmt, _In_ SQLSMALLINT rec_num, _In_ SQLSMALLINT fld_id, _In_reads_bytes_opt_( str_len ) SQLPOINTER value_ptr, _In_ SQLINTEGER str_len TSRMLS_DC ) { SQLRETURN r; SQLHDESC hIpd = NULL; - core::SQLGetStmtAttr(stmt, SQL_ATTR_IMP_PARAM_DESC, &hIpd, 0, 0); - r = ::SQLSetDescField(hIpd, rec_num, fld_id, value_ptr, str_len); + core::SQLGetStmtAttr( stmt, SQL_ATTR_IMP_PARAM_DESC, &hIpd, 0, 0 ); + r = ::SQLSetDescField( hIpd, rec_num, fld_id, value_ptr, str_len ); - CHECK_SQL_ERROR_OR_WARNING(r, stmt) { + CHECK_SQL_ERROR_OR_WARNING( r, stmt ) { throw CoreException(); } } diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index 26a61f43..79913e6a 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -559,6 +559,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_ // 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 // output parameters. + // if column encryption is enabled, do not set the length to unlimited since SQLDescribe paramter already derive the length for us if( direction == SQL_PARAM_OUTPUT && !stmt->conn->ce_option.enabled ) { switch( sql_type ) { @@ -2530,7 +2531,7 @@ void resize_output_buffer_if_necessary( _Inout_ sqlsrv_stmt* stmt, _Inout_ zval* SQLULEN field_size = column_size; // with AE on, when column_size is retrieved from SQLDescribeParam, column_size does not include the decimal place // include the decimal for output params by adding elem_size - if ( stmt->conn->ce_option.enabled && decimal_digits != 0 ) + if ( stmt->conn->ce_option.enabled && decimal_digits > 0 ) { field_size += elem_size; } From f2381ba7b26fe4c00ed7848c79e5e08481d1a1ba Mon Sep 17 00:00:00 2001 From: v-kaywon Date: Mon, 11 Sep 2017 13:06:20 -0700 Subject: [PATCH 4/7] change comment --- source/shared/core_stmt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index 79913e6a..6db8b16c 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -559,7 +559,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_ // 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 // output parameters. - // if column encryption is enabled, do not set the length to unlimited since SQLDescribe paramter already derive the length for us + // if column encryption is enabled, at this point the correct column size has been set by SQLDescribeParam if( direction == SQL_PARAM_OUTPUT && !stmt->conn->ce_option.enabled ) { switch( sql_type ) { From ba7684e8c22fece8c18532698d370fee3a31a99e Mon Sep 17 00:00:00 2001 From: v-kaywon Date: Mon, 11 Sep 2017 13:41:43 -0700 Subject: [PATCH 5/7] change comment --- source/shared/core_stmt.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index 6db8b16c..6e4279f0 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -2024,8 +2024,9 @@ void ae_get_sql_type_info( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno, { SQLSMALLINT Nullable; core::SQLDescribeParam( stmt, paramno + 1, &sql_type, &column_size, &decimal_digits, &Nullable ); - // without AE, binding an INPUT bigint with sql type SQL_WVARCHAR returns a conversion error between nvarchar and bigint - // similate without AE by setting the sql type back to SQL_WVARCHAR using default_sql_type + // when column is not encrypted, binding an INPUT bigint with sql type SQL_WVARCHAR returns a conversion error between nvarchar and bigint + // at thie point after calling SQLDescribeParam, the sql_type is now SQL_BIGINT for a BIGINT column + // to simulate the behavior of binding a non-encrypted column, set the sql type back to SQL_WVARCHAR or SQL_VARCHAR using default_sql_type if ( sql_type == SQL_BIGINT && Z_TYPE_P(param_z) == IS_STRING && direction == SQL_PARAM_INPUT ) { default_sql_type( stmt, paramno, param_z, encoding, sql_type TSRMLS_CC ); default_sql_size_and_scale( stmt, paramno, param_z, encoding, column_size, decimal_digits ); From 3ddb8aae78140efca8df16e70e8543f33963fa24 Mon Sep 17 00:00:00 2001 From: v-kaywon Date: Mon, 11 Sep 2017 15:41:19 -0700 Subject: [PATCH 6/7] change conditions for using sql types derived from SQLDescribeParam --- source/shared/core_stmt.cpp | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index 6e4279f0..9a12e4d3 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -438,8 +438,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_ ( encoding == SQLSRV_ENCODING_SYSTEM || encoding == SQLSRV_ENCODING_UTF8 || encoding == SQLSRV_ENCODING_BINARY ), "core_sqlsrv_bind_param: invalid encoding" ); - if ( stmt->conn->ce_option.enabled && ( sql_type == SQL_UNKNOWN_TYPE || column_size == SQLSRV_UNKNOWN_SIZE ) && - Z_TYPE_P( param_z ) != IS_NULL && Z_TYPE_P( param_z ) != IS_FALSE && Z_TYPE_P( param_z) != IS_TRUE ) { + if ( stmt->conn->ce_option.enabled && ( sql_type == SQL_UNKNOWN_TYPE || column_size == SQLSRV_UNKNOWN_SIZE )) { ae_get_sql_type_info( stmt, param_num, direction, param_z, encoding, sql_type, column_size, decimal_digits TSRMLS_CC ); } else { @@ -2024,13 +2023,6 @@ void ae_get_sql_type_info( _Inout_ sqlsrv_stmt* stmt, _In_opt_ SQLULEN paramno, { SQLSMALLINT Nullable; core::SQLDescribeParam( stmt, paramno + 1, &sql_type, &column_size, &decimal_digits, &Nullable ); - // when column is not encrypted, binding an INPUT bigint with sql type SQL_WVARCHAR returns a conversion error between nvarchar and bigint - // at thie point after calling SQLDescribeParam, the sql_type is now SQL_BIGINT for a BIGINT column - // to simulate the behavior of binding a non-encrypted column, set the sql type back to SQL_WVARCHAR or SQL_VARCHAR using default_sql_type - if ( sql_type == SQL_BIGINT && Z_TYPE_P(param_z) == IS_STRING && direction == SQL_PARAM_INPUT ) { - default_sql_type( stmt, paramno, param_z, encoding, sql_type TSRMLS_CC ); - default_sql_size_and_scale( stmt, paramno, param_z, encoding, column_size, decimal_digits ); - } } void col_cache_dtor( _Inout_ zval* data_z ) From 5fb49b8c77803d5e019117afce7e95dcfb6f3e2f Mon Sep 17 00:00:00 2001 From: v-kaywon Date: Tue, 12 Sep 2017 09:42:13 -0700 Subject: [PATCH 7/7] change typo in error message length --- source/shared/core_util.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/shared/core_util.cpp b/source/shared/core_util.cpp index 5230a8a2..c5f614f4 100644 --- a/source/shared/core_util.cpp +++ b/source/shared/core_util.cpp @@ -220,7 +220,7 @@ bool core_sqlsrv_get_odbc_error( _Inout_ sqlsrv_context& ctx, _In_ int record_nu SQLRETURN r = SQL_SUCCESS; SQLSMALLINT wmessage_len = 0; SQLWCHAR wsqlstate[ SQL_SQLSTATE_BUFSIZE ] = { L'\0' }; - SQLWCHAR wnative_message[ ( SQL_MAX_ERROR_MESSAGE_LENGTH + 1 ) * 2 ] = { L'\0' }; + SQLWCHAR wnative_message[ SQL_MAX_ERROR_MESSAGE_LENGTH + 1 ] = { L'\0' }; SQLSRV_ENCODING enc = ctx.encoding(); switch( h_type ) { @@ -245,7 +245,7 @@ bool core_sqlsrv_get_odbc_error( _Inout_ sqlsrv_context& ctx, _In_ int record_nu default: error = new ( sqlsrv_malloc( sizeof( sqlsrv_error ))) sqlsrv_error(); r = SQLGetDiagRecW( h_type, h, record_number, wsqlstate, &error->native_code, wnative_message, - (SQL_MAX_ERROR_MESSAGE_LENGTH + 1)*2, &wmessage_len ); + SQL_MAX_ERROR_MESSAGE_LENGTH + 1, &wmessage_len ); // don't use the CHECK* macros here since it will trigger reentry into the error handling system // Workaround for a bug in unixODBC 2.3.4 when connection pooling is enabled (PDO SQLSRV). // Instead of returning false, we return an empty error message to prevent the driver from throwing an exception.