diff --git a/source/shared/core_sqlsrv.h b/source/shared/core_sqlsrv.h index d950b1ff..382b7ace 100644 --- a/source/shared/core_sqlsrv.h +++ b/source/shared/core_sqlsrv.h @@ -1315,11 +1315,10 @@ struct sqlsrv_output_param { 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; - bool is_long; // string output param constructor - sqlsrv_output_param( _In_ zval* p_z, _In_ SQLSRV_ENCODING enc, _In_ int num, _In_ SQLUINTEGER buffer_len, _In_ bool is_long ) : - param_z( p_z ), encoding( enc ), param_num( num ), original_buffer_len( buffer_len ), is_bool( false ), is_long( is_long ) + sqlsrv_output_param( _In_ zval* p_z, _In_ SQLSRV_ENCODING enc, _In_ int num, _In_ SQLUINTEGER buffer_len ) : + param_z( p_z ), encoding( enc ), param_num( num ), original_buffer_len( buffer_len ), is_bool( false ) { } @@ -1329,8 +1328,7 @@ struct sqlsrv_output_param { encoding( SQLSRV_ENCODING_INVALID ), param_num( num ), original_buffer_len( -1 ), - is_bool( is_bool ), - is_long( false ) + is_bool( is_bool ) { } }; diff --git a/source/shared/core_stmt.cpp b/source/shared/core_stmt.cpp index e5f66d31..ba2deca9 100644 --- a/source/shared/core_stmt.cpp +++ b/source/shared/core_stmt.cpp @@ -372,7 +372,6 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_ } 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_long = ( Z_TYPE_P( param_z ) == IS_LONG && php_out_type == SQLSRV_PHPTYPE_INT && (sql_type == SQL_BIGINT || sql_type == SQL_UNKNOWN_TYPE )); // 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. @@ -383,16 +382,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_ if( zval_was_null || zval_was_bool ){ convert_to_long( param_z ); } - if( zval_was_long ){ - convert_to_string( param_z ); - if( encoding != SQLSRV_ENCODING_SYSTEM && encoding != SQLSRV_ENCODING_UTF8 && encoding != SQLSRV_ENCODING_BINARY ){ - encoding = SQLSRV_ENCODING_SYSTEM; - } - match = Z_TYPE_P( param_z ) == IS_STRING; - } - else{ - match = Z_TYPE_P( param_z ) == IS_LONG; - } + match = Z_TYPE_P( param_z ) == IS_LONG; break; case SQLSRV_PHPTYPE_FLOAT: if( zval_was_null ){ @@ -431,15 +421,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_ if( direction == SQL_PARAM_OUTPUT ){ switch( php_out_type ) { case SQLSRV_PHPTYPE_INT: - if( zval_was_long ){ - convert_to_string( param_z ); - if( encoding != SQLSRV_ENCODING_SYSTEM && encoding != SQLSRV_ENCODING_UTF8 && encoding != SQLSRV_ENCODING_BINARY ){ - encoding = SQLSRV_ENCODING_SYSTEM; - } - } - else{ - convert_to_long( param_z ); - } + convert_to_long( param_z ); break; case SQLSRV_PHPTYPE_FLOAT: convert_to_double( param_z ); @@ -572,7 +554,7 @@ void core_sqlsrv_bind_param( _Inout_ sqlsrv_stmt* stmt, _In_ SQLUSMALLINT param_ 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() ){ + param_num + 1, get_last_error_message() ){ throw core::CoreException(); } buffer = Z_STRVAL_P( param_z ); @@ -583,10 +565,10 @@ 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, decimal_digits, - buffer, buffer_len TSRMLS_CC ); + 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, static_cast( buffer_len ), zval_was_long ); + sqlsrv_output_param output_param( param_ref, encoding, param_num, static_cast( buffer_len ) ); save_output_param_for_later( stmt, output_param TSRMLS_CC ); @@ -2151,15 +2133,6 @@ void finalize_output_parameters( _Inout_ sqlsrv_stmt* stmt TSRMLS_DC ) else { core::sqlsrv_zval_stringl(value_z, str, str_len); } - if ( output_param->is_long ) { - zval* value_z_temp = ( zval * )sqlsrv_malloc( sizeof( zval )); - ZVAL_COPY( value_z_temp, value_z ); - convert_to_double( value_z_temp ); - if ( Z_DVAL_P( value_z_temp ) > INT_MIN && Z_DVAL_P( value_z_temp ) < INT_MAX ) { - convert_to_long( value_z ); - } - sqlsrv_free( value_z_temp ); - } } break; case IS_LONG: diff --git a/test/functional/pdo_sqlsrv/pdo_ae_output_param_binary_size.phpt b/test/functional/pdo_sqlsrv/pdo_ae_output_param_binary_size.phpt index de999f2d..6feb6a50 100644 --- a/test/functional/pdo_sqlsrv/pdo_ae_output_param_binary_size.phpt +++ b/test/functional/pdo_sqlsrv/pdo_ae_output_param_binary_size.phpt @@ -88,9 +88,25 @@ function testOutputBinary($inout) $stmt = $conn->prepare($outSql); trace("\nParam $pdoParamType with INOUT = $inout\n"); - if ($inout && $pdoParamType != PDO::PARAM_STR) { + if ($inout && $pdoParamType == PDO::PARAM_STR) { // Currently do not support getting binary as strings + INOUT param // See VSO 2829 for details + continue; + } + + if (!isAEConnected() && $pdoParamType == PDO::PARAM_INT) { + // Without AE, there is no possible way to specify + // binary encoding for this output param type, + // so a value like 'd' will be incorrectly + // interpreted as integer value 100 (its ASCII value). + // Skipping this because this use case is meaningless + // anyway. + // With AE, this output param type would have caused + // an exception + continue; + } + + if ($inout) { $paramType = $pdoParamType | PDO::PARAM_INPUT_OUTPUT; } else { $paramType = $pdoParamType; @@ -101,14 +117,11 @@ function testOutputBinary($inout) if ($pdoParamType == PDO::PARAM_STR || $pdoParamType == PDO::PARAM_LOB) { $stmt->bindParam(1, $det, $paramType, $length, PDO::SQLSRV_ENCODING_BINARY); $stmt->bindParam(2, $rand, $paramType, $length, PDO::SQLSRV_ENCODING_BINARY); - } elseif ($pdoParamType == PDO::PARAM_BOOL || $pdoParamType == PDO::PARAM_INT) { + } else { $det = $rand = 0; $stmt->bindParam(1, $det, $paramType, PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE); $stmt->bindParam(2, $rand, $paramType, PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE); - } else { - $stmt->bindParam(1, $det, $paramType, $length); - $stmt->bindParam(2, $rand, $paramType, $length); - } + } try { $stmt->execute(); @@ -127,12 +140,8 @@ function testOutputBinary($inout) printValues($errMsg, $det, $rand, $input0, $input1); } } else { - // $pdoParamType is PDO::PARAM_INT - // This only occurs without AE -- likely a rare use case - // With AE enabled, this would have caused an exception - if (strval($det) != $ord0 || strval($rand) != $ord1) { - printValues($errMsg, $det, $rand, $ord0, $ord1); - } + echo "Something went wrong. This should not have happened\n"; + printValues($errMsg, $det, $rand, $ord0, $ord1); } } catch (PDOException $e) { $message = $e->getMessage(); @@ -147,20 +156,13 @@ function testOutputBinary($inout) } } elseif ($pdoParamType == PDO::PARAM_BOOL || PDO::PARAM_INT) { if (isAEConnected()) { - if ($pdoParamType == PDO::PARAM_INT) { - // Expected to fail with this message - $error = "String data, right truncated for output parameter"; - $found = strpos($message, $error); - } else { - // PDO::PARAM_BOOL - - // Expected error 07006 with AE enabled: - // "Restricted data type attribute violation" - // The data value returned for a parameter bound as - // SQL_PARAM_INPUT_OUTPUT or SQL_PARAM_OUTPUT could not - // be converted to the data type identified by the - // ValueType argument in SQLBindParameter. - $found = strpos($message, $errors['07006']); - } + // Expected error 07006 with AE enabled: + // "Restricted data type attribute violation" + // The data value returned for a parameter bound as + // SQL_PARAM_INPUT_OUTPUT or SQL_PARAM_OUTPUT could not + // be converted to the data type identified by the + // ValueType argument in SQLBindParameter. + $found = strpos($message, $errors['07006']); } else { // When not AE enabled, expected to fail with something like this message // "Implicit conversion from data type nvarchar(max) to binary is not allowed. Use the CONVERT function to run this query." @@ -169,6 +171,7 @@ function testOutputBinary($inout) $found = strpos($message, $error); } if ($found === false) { + print "Exception: $message\n"; printValues($errMsg, $det, $rand, $input0, $input1); } } else { @@ -207,4 +210,4 @@ echo "Done\n"; unset($conn); ?> --EXPECT-- -Done \ No newline at end of file +Done diff --git a/test/functional/pdo_sqlsrv/pdo_ae_output_param_char_size.phpt b/test/functional/pdo_sqlsrv/pdo_ae_output_param_char_size.phpt index 66a68004..81e79255 100644 --- a/test/functional/pdo_sqlsrv/pdo_ae_output_param_char_size.phpt +++ b/test/functional/pdo_sqlsrv/pdo_ae_output_param_char_size.phpt @@ -120,7 +120,7 @@ function testOutputChars($inout) if ($found === false) { printValues($errMsg, $det, $rand, $input0, $input1); } - } elseif ($pdoParamType == PDO::PARAM_BOOL) { + } elseif ($pdoParamType == PDO::PARAM_BOOL || $pdoParamType == PDO::PARAM_INT) { if (isAEConnected()) { // Expected error 22003: "Numeric value out of range" $found = strpos($message, $errors['22003']); diff --git a/test/functional/pdo_sqlsrv/pdo_ae_output_param_datetimes.phpt b/test/functional/pdo_sqlsrv/pdo_ae_output_param_datetimes.phpt index 02e2c83f..dfd804b4 100644 --- a/test/functional/pdo_sqlsrv/pdo_ae_output_param_datetimes.phpt +++ b/test/functional/pdo_sqlsrv/pdo_ae_output_param_datetimes.phpt @@ -146,7 +146,7 @@ function testOutputDatetimes($inout) // streams cannot be specified as output parameters." $found = strpos($message, $errors['IMSSP']); } elseif (isAEConnected()) { - if ($pdoParamType == PDO::PARAM_BOOL) { + if ($pdoParamType != PDO::PARAM_STR) { // Expected error 07006: "Restricted data type attribute violation" // What does this error mean? // The data value returned for a parameter bound as @@ -159,7 +159,7 @@ function testOutputDatetimes($inout) $found = strpos($message, $error); } } else { - if ($pdoParamType == PDO::PARAM_BOOL) { + if ($pdoParamType != PDO::PARAM_STR) { $error = "Operand type clash: int is incompatible with $dataType"; } else { $error = "Error converting data type nvarchar to $dataType"; @@ -167,6 +167,7 @@ function testOutputDatetimes($inout) $found = strpos($message, $error); } if ($found === false) { + print $message . PHP_EOL; printValues($errMsg, $det, $rand, $inputValues); } } diff --git a/test/functional/pdo_sqlsrv/pdo_ae_output_param_decimals.phpt b/test/functional/pdo_sqlsrv/pdo_ae_output_param_decimals.phpt index 4f30c1a2..fce2ee4d 100644 --- a/test/functional/pdo_sqlsrv/pdo_ae_output_param_decimals.phpt +++ b/test/functional/pdo_sqlsrv/pdo_ae_output_param_decimals.phpt @@ -56,14 +56,9 @@ function compareIntegers($det, $rand, $inputValues, $pdoParamType) if (is_string($det)) { return (!compareFloats(floatval($det), $inputValues[0]) && !compareFloats(floatval($rand), $inputValues[1])); - } elseif ($pdoParamType == PDO::PARAM_INT) { - $input0 = floor($inputValues[0]); // the positive float - $input1 = ceil($inputValues[1]); // the negative float - - return ($det == $input0 && $rand == $input1); } else { - // $pdoParamType == PDO::PARAM_BOOL - // Expect bool(true) or bool(false) depending on the rounded input values + // if $pdoParamType is PDO::PARAM_BOOL, + // expect bool(true) or bool(false) depending on the rounded input values // But with AE enabled (aforementioned GitHub issue), the fetched values // are floats instead, which should be fixed $input0 = floor($inputValues[0]); // the positive float @@ -195,9 +190,9 @@ function testOutputDecimals($inout) if ($found === false) { printValues($errMsg, $det, $rand, $inputValues); } - } elseif (!isAEConnected() && $precision >= 16 && $pdoParamType == PDO::PARAM_BOOL) { + } elseif (!isAEConnected() && $precision >= 16) { // When not AE enabled, large numbers are expected to - // fail when converting to booleans + // fail when converting to booleans / integers $error = "Error converting data type $dataType to int"; $found = strpos($message, $error); if ($found === false) { diff --git a/test/functional/pdo_sqlsrv/pdo_ae_output_param_floats.phpt b/test/functional/pdo_sqlsrv/pdo_ae_output_param_floats.phpt index 54a5e4ac..61fca7c4 100644 --- a/test/functional/pdo_sqlsrv/pdo_ae_output_param_floats.phpt +++ b/test/functional/pdo_sqlsrv/pdo_ae_output_param_floats.phpt @@ -85,13 +85,6 @@ function testOutputFloats($fetchNumeric, $inout) // call stored procedure $outSql = getCallProcSqlPlaceholders($spname, 2); foreach ($pdoParamTypes as $pdoParamType) { - if ($pdoParamType == PDO::PARAM_INT && (strtoupper(substr(PHP_OS, 0, 3)) != 'WIN' || substr(PHP_VERSION, 0, 3) == "7.0")) { - // Bug 2876 in VSO: Linux or PHP 7.0 - when retrieving a float as OUTPUT - // or INOUT parameter with PDO::PARAM_INT, the returned values - // are always single digits, regardless of the original floats - continue; - } - $det = 0.0; $rand = 0.0; $stmt = $conn->prepare($outSql); diff --git a/test/functional/pdo_sqlsrv/pdo_ae_output_param_nchar_size.phpt b/test/functional/pdo_sqlsrv/pdo_ae_output_param_nchar_size.phpt index e6da0d9c..5e52558b 100644 --- a/test/functional/pdo_sqlsrv/pdo_ae_output_param_nchar_size.phpt +++ b/test/functional/pdo_sqlsrv/pdo_ae_output_param_nchar_size.phpt @@ -121,7 +121,7 @@ function testOutputNChars($inout) if ($found === false) { printValues($errMsg, $det, $rand, $input0, $input1); } - } elseif ($pdoParamType == PDO::PARAM_BOOL) { + } elseif ($pdoParamType == PDO::PARAM_BOOL || $pdoParamType == PDO::PARAM_INT) { if (isAEConnected()) { // Expected error 22003: "Numeric value out of range" $found = strpos($message, $errors['22003']); diff --git a/test/functional/pdo_sqlsrv/pdo_bigint_outparam.phpt b/test/functional/pdo_sqlsrv/pdo_bigint_outparam.phpt index 58c370d9..2de34584 100644 --- a/test/functional/pdo_sqlsrv/pdo_bigint_outparam.phpt +++ b/test/functional/pdo_sqlsrv/pdo_bigint_outparam.phpt @@ -26,7 +26,7 @@ insertRow($conn, $tbname, array("c1_bigint" => 922337203685479936)); $outSql = "{CALL $spname (?)}"; $bigintOut = 0; $stmt = $conn->prepare($outSql); -$stmt->bindParam(1, $bigintOut, PDO::PARAM_INT, PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE); +$stmt->bindParam(1, $bigintOut, PDO::PARAM_STR, 32); $stmt->execute(); printf("Large bigint output:\n" ); var_dump($bigintOut); @@ -35,7 +35,7 @@ printf("\n"); // Call stored procedure with inout $bigintOut = 0; $stmt = $conn->prepare($outSql); -$stmt->bindParam(1, $bigintOut, PDO::PARAM_INT | PDO::PARAM_INPUT_OUTPUT, PDO::SQLSRV_PARAM_OUT_DEFAULT_SIZE); +$stmt->bindParam(1, $bigintOut, PDO::PARAM_STR | PDO::PARAM_INPUT_OUTPUT, 2048); $stmt->execute(); printf("Large bigint inout:\n" ); var_dump($bigintOut); diff --git a/test/functional/sqlsrv/sqlsrv_bigint_outparam.phpt b/test/functional/sqlsrv/sqlsrv_bigint_outparam.phpt index f305ecee..83239cd7 100644 --- a/test/functional/sqlsrv/sqlsrv_bigint_outparam.phpt +++ b/test/functional/sqlsrv/sqlsrv_bigint_outparam.phpt @@ -25,7 +25,7 @@ AE\insertRow($conn, $tbname, array("c1_bigint" => 922337203685479936)); // Call stored procedure with SQLSRV_PARAM_OUT $outSql = "{CALL $spname (?)}"; $bigintOut = 0; -$stmt = sqlsrv_prepare($conn, $outSql, array(array(&$bigintOut, SQLSRV_PARAM_OUT))); +$stmt = sqlsrv_prepare($conn, $outSql, array(array(&$bigintOut, SQLSRV_PARAM_OUT, null, SQLSRV_SQLTYPE_BIGINT))); sqlsrv_execute($stmt); printf("Large bigint output:\n"); var_dump($bigintOut); @@ -33,7 +33,7 @@ printf("\n"); // Call stored procedure with SQLSRV_PARAM_INOUT $bigintOut = 0; -$stmt = sqlsrv_prepare($conn, $outSql, array(array(&$bigintOut, SQLSRV_PARAM_INOUT))); +$stmt = sqlsrv_prepare($conn, $outSql, array(array(&$bigintOut, SQLSRV_PARAM_INOUT, null, SQLSRV_SQLTYPE_VARCHAR(32)))); sqlsrv_execute($stmt); printf("Large bigint inout:\n"); var_dump($bigintOut);