From 5f555dfea275b2dfd11091227c2f0eaa25af1fb4 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Wed, 2 May 2018 11:40:55 -0700 Subject: [PATCH 1/5] Modified the expected results for a bind column test --- ...bindColumn_pdoparam_decimal_precision.phpt | 225 +++++------------- 1 file changed, 62 insertions(+), 163 deletions(-) diff --git a/test/functional/pdo_sqlsrv/pdo_ae_bindColumn_pdoparam_decimal_precision.phpt b/test/functional/pdo_sqlsrv/pdo_ae_bindColumn_pdoparam_decimal_precision.phpt index 247d298d..ce4aa395 100644 --- a/test/functional/pdo_sqlsrv/pdo_ae_bindColumn_pdoparam_decimal_precision.phpt +++ b/test/functional/pdo_sqlsrv/pdo_ae_bindColumn_pdoparam_decimal_precision.phpt @@ -1,17 +1,10 @@ --TEST-- Test for retrieving encrypted data from decimal types columns using PDO::bindColumn --DESCRIPTION-- -Test conversion from decimal types column to output of PDO::PARAM types -With or without ALways Encrypted, conversion works if: -1. From any decimal type column to PDO::PARAM_STR -2. From any decimal type column to PDO::PARAM_LOB -TODO: behavior for teching decimals as PARAM_BOOL and PARAM_INT varies depending on the number being fetched - 1. if the number is less than 1, returns 0 (even though the number being fetched is 0.9) - 2. if the number is greater than 1 and the number of digits is less than 11, returns the correctly rounded integer (e.g., returns 922 when fetching 922.3) - 3. if the number is greater than 1 and the number of digits is greater than 11, returns NULL - need to investigate which should be the correct behavior - for this test, assume to correct behavior is to return NULL - documented in VSO 2730 +Test conversion from decimal types to output of PDO::PARAM types +With or without Always Encrypted, conversion should work for all PDO::PARAM +types unless for cases when mapping large decimal / numeric values to +integers (values out of range) --SKIPIF-- --FILE-- @@ -27,6 +20,37 @@ $precisions = array(1 => array(0, 1), $inputValuesInit = array(92233720368547758089223372036854775808, -92233720368547758089223372036854775808); $inputPrecision = 38; +// function checkNULLs() returns false when at least one of fetched +// values is not null +function checkNULLs($pdoParamType, $typeFull, $det, $rand) +{ + if (!is_null($det) || !is_null($rand)) { + echo "Retrieving $typeFull data as $pdoParamType should return NULL\n"; + return false; + } + return true; +} + +// function compareIntegers() returns false when the fetched values +// are different from the expected inputs +function compareIntegers($pdoParamType, $det, $rand, $inputValues) +{ + // Assuming $pdoParamType is PDO::PARAM_BOOL or PDO::PARAM_INT + $input0 = floor($inputValues[0]); // the positive float + $input1 = ceil($inputValues[1]); // the negative float + + $matched = ($det == $input0 && $rand == $input1); + if (!$matched) { + echo "****Binding as $pdoParamType failed:****\n"; + echo "input 0: "; var_dump($inputValues[0]); + echo "fetched: "; var_dump($det); + echo "input 1: "; var_dump($inputValues[1]); + echo "fetched: "; var_dump($rand); + } + + return $matched; +} + try { $conn = connect("", array(), PDO::ERRMODE_SILENT); foreach ($dataTypes as $dataType) { @@ -40,7 +64,7 @@ try { } // compute the epsilon for comparing doubles - // float in PHP only has a precision of roughtly 14 digits: http://php.net/manual/en/language.types.float.php + // float in PHP only has a precision of roughly 14 digits: http://php.net/manual/en/language.types.float.php $epsilon; if ($m1 < 14) { $epsilon = pow(10, $m2 * -1); @@ -54,9 +78,9 @@ try { } $typeFull = "$dataType($m1, $m2)"; - echo "\nTesting $typeFull:\n"; - // create and populate table containing decimal(m1, m2) or numeric(m1, m2) columns + // create and populate table containing decimal(m1, m2) + // or numeric(m1, m2) columns $tbname = "test_" . $dataType . $m1 . $m2; $colMetaArr = array(new ColumnMeta($typeFull, "c_det"), new ColumnMeta($typeFull, "c_rand", null, "randomized")); createTable($conn, $tbname, $colMetaArr); @@ -68,25 +92,36 @@ try { $det = ""; $rand = ""; $stmt = $conn->prepare($query); + $stmt->execute(); $stmt->bindColumn('c_det', $det, constant($pdoParamType)); $stmt->bindColumn('c_rand', $rand, constant($pdoParamType)); $row = $stmt->fetch(PDO::FETCH_BOUND); - // check the case when fetching as PDO::PARAM_BOOL, PDO::PARAM_NULL or PDO::PARAM_INT - // with or without AE: should not work - // assume to correct behavior is to return NULL, see description - if ($pdoParamType == "PDO::PARAM_BOOL" || $pdoParamType == "PDO::PARAM_NULL" || $pdoParamType == "PDO::PARAM_INT") { - if (!is_null($det) || !is_null($rand)) { - echo "Retrieving $typeFull data as $pdoParamType should return NULL\n"; + // With AE or not, the behavior is the same + $succeeded = false; + if ($pdoParamType == "PDO::PARAM_NULL") { + $succeeded = checkNULLs($pdoParamType, $typeFull, $det, $rand); + } elseif ($pdoParamType == "PDO::PARAM_BOOL" || $pdoParamType == "PDO::PARAM_INT") { + if ($m1 >= 16 && ($m1 != $m2)) { + // When the precision is more than 16 (unless the + // precision = scale), the returned values are + // out of range as integers, so expect NULL + // (the data retrieval should have caused + // an exception but was silenced) + $succeeded = checkNULLs($pdoParamType, $typeFull, $det, $rand); + } else { + $succeeded = compareIntegers($pdoParamType, $det, $rand, $inputValues, $m1, $m2); } } else { if (abs($det - $inputValues[0]) < $epsilon && abs($rand - $inputValues[1]) < $epsilon) { - echo "****Retrieving $typeFull as $pdoParamType is supported****\n"; - } else { - echo "Retrieving $typeFull as $pdoParamType fails\n"; - } + $succeeded = true; + } + } + + if (!$succeeded) { + echo "Retrieving $typeFull as $pdoParamType fails\n"; } } // cleanup @@ -96,147 +131,11 @@ try { } unset($stmt); unset($conn); + + echo "Test successfully done\n"; } catch (PDOException $e) { echo $e->getMessage(); } ?> --EXPECT-- -Testing decimal(1, 0): -Retrieving decimal(1, 0) data as PDO::PARAM_BOOL should return NULL -Retrieving decimal(1, 0) data as PDO::PARAM_INT should return NULL -****Retrieving decimal(1, 0) as PDO::PARAM_STR is supported**** -****Retrieving decimal(1, 0) as PDO::PARAM_LOB is supported**** - -Testing decimal(1, 1): -Retrieving decimal(1, 1) data as PDO::PARAM_BOOL should return NULL -Retrieving decimal(1, 1) data as PDO::PARAM_INT should return NULL -****Retrieving decimal(1, 1) as PDO::PARAM_STR is supported**** -****Retrieving decimal(1, 1) as PDO::PARAM_LOB is supported**** - -Testing decimal(4, 0): -Retrieving decimal(4, 0) data as PDO::PARAM_BOOL should return NULL -Retrieving decimal(4, 0) data as PDO::PARAM_INT should return NULL -****Retrieving decimal(4, 0) as PDO::PARAM_STR is supported**** -****Retrieving decimal(4, 0) as PDO::PARAM_LOB is supported**** - -Testing decimal(4, 1): -Retrieving decimal(4, 1) data as PDO::PARAM_BOOL should return NULL -Retrieving decimal(4, 1) data as PDO::PARAM_INT should return NULL -****Retrieving decimal(4, 1) as PDO::PARAM_STR is supported**** -****Retrieving decimal(4, 1) as PDO::PARAM_LOB is supported**** - -Testing decimal(4, 4): -Retrieving decimal(4, 4) data as PDO::PARAM_BOOL should return NULL -Retrieving decimal(4, 4) data as PDO::PARAM_INT should return NULL -****Retrieving decimal(4, 4) as PDO::PARAM_STR is supported**** -****Retrieving decimal(4, 4) as PDO::PARAM_LOB is supported**** - -Testing decimal(16, 0): -****Retrieving decimal(16, 0) as PDO::PARAM_STR is supported**** -****Retrieving decimal(16, 0) as PDO::PARAM_LOB is supported**** - -Testing decimal(16, 1): -****Retrieving decimal(16, 1) as PDO::PARAM_STR is supported**** -****Retrieving decimal(16, 1) as PDO::PARAM_LOB is supported**** - -Testing decimal(16, 4): -****Retrieving decimal(16, 4) as PDO::PARAM_STR is supported**** -****Retrieving decimal(16, 4) as PDO::PARAM_LOB is supported**** - -Testing decimal(16, 16): -Retrieving decimal(16, 16) data as PDO::PARAM_BOOL should return NULL -Retrieving decimal(16, 16) data as PDO::PARAM_INT should return NULL -****Retrieving decimal(16, 16) as PDO::PARAM_STR is supported**** -****Retrieving decimal(16, 16) as PDO::PARAM_LOB is supported**** - -Testing decimal(38, 0): -****Retrieving decimal(38, 0) as PDO::PARAM_STR is supported**** -****Retrieving decimal(38, 0) as PDO::PARAM_LOB is supported**** - -Testing decimal(38, 1): -****Retrieving decimal(38, 1) as PDO::PARAM_STR is supported**** -****Retrieving decimal(38, 1) as PDO::PARAM_LOB is supported**** - -Testing decimal(38, 4): -****Retrieving decimal(38, 4) as PDO::PARAM_STR is supported**** -****Retrieving decimal(38, 4) as PDO::PARAM_LOB is supported**** - -Testing decimal(38, 16): -****Retrieving decimal(38, 16) as PDO::PARAM_STR is supported**** -****Retrieving decimal(38, 16) as PDO::PARAM_LOB is supported**** - -Testing decimal(38, 38): -Retrieving decimal(38, 38) data as PDO::PARAM_BOOL should return NULL -Retrieving decimal(38, 38) data as PDO::PARAM_INT should return NULL -****Retrieving decimal(38, 38) as PDO::PARAM_STR is supported**** -****Retrieving decimal(38, 38) as PDO::PARAM_LOB is supported**** - -Testing numeric(1, 0): -Retrieving numeric(1, 0) data as PDO::PARAM_BOOL should return NULL -Retrieving numeric(1, 0) data as PDO::PARAM_INT should return NULL -****Retrieving numeric(1, 0) as PDO::PARAM_STR is supported**** -****Retrieving numeric(1, 0) as PDO::PARAM_LOB is supported**** - -Testing numeric(1, 1): -Retrieving numeric(1, 1) data as PDO::PARAM_BOOL should return NULL -Retrieving numeric(1, 1) data as PDO::PARAM_INT should return NULL -****Retrieving numeric(1, 1) as PDO::PARAM_STR is supported**** -****Retrieving numeric(1, 1) as PDO::PARAM_LOB is supported**** - -Testing numeric(4, 0): -Retrieving numeric(4, 0) data as PDO::PARAM_BOOL should return NULL -Retrieving numeric(4, 0) data as PDO::PARAM_INT should return NULL -****Retrieving numeric(4, 0) as PDO::PARAM_STR is supported**** -****Retrieving numeric(4, 0) as PDO::PARAM_LOB is supported**** - -Testing numeric(4, 1): -Retrieving numeric(4, 1) data as PDO::PARAM_BOOL should return NULL -Retrieving numeric(4, 1) data as PDO::PARAM_INT should return NULL -****Retrieving numeric(4, 1) as PDO::PARAM_STR is supported**** -****Retrieving numeric(4, 1) as PDO::PARAM_LOB is supported**** - -Testing numeric(4, 4): -Retrieving numeric(4, 4) data as PDO::PARAM_BOOL should return NULL -Retrieving numeric(4, 4) data as PDO::PARAM_INT should return NULL -****Retrieving numeric(4, 4) as PDO::PARAM_STR is supported**** -****Retrieving numeric(4, 4) as PDO::PARAM_LOB is supported**** - -Testing numeric(16, 0): -****Retrieving numeric(16, 0) as PDO::PARAM_STR is supported**** -****Retrieving numeric(16, 0) as PDO::PARAM_LOB is supported**** - -Testing numeric(16, 1): -****Retrieving numeric(16, 1) as PDO::PARAM_STR is supported**** -****Retrieving numeric(16, 1) as PDO::PARAM_LOB is supported**** - -Testing numeric(16, 4): -****Retrieving numeric(16, 4) as PDO::PARAM_STR is supported**** -****Retrieving numeric(16, 4) as PDO::PARAM_LOB is supported**** - -Testing numeric(16, 16): -Retrieving numeric(16, 16) data as PDO::PARAM_BOOL should return NULL -Retrieving numeric(16, 16) data as PDO::PARAM_INT should return NULL -****Retrieving numeric(16, 16) as PDO::PARAM_STR is supported**** -****Retrieving numeric(16, 16) as PDO::PARAM_LOB is supported**** - -Testing numeric(38, 0): -****Retrieving numeric(38, 0) as PDO::PARAM_STR is supported**** -****Retrieving numeric(38, 0) as PDO::PARAM_LOB is supported**** - -Testing numeric(38, 1): -****Retrieving numeric(38, 1) as PDO::PARAM_STR is supported**** -****Retrieving numeric(38, 1) as PDO::PARAM_LOB is supported**** - -Testing numeric(38, 4): -****Retrieving numeric(38, 4) as PDO::PARAM_STR is supported**** -****Retrieving numeric(38, 4) as PDO::PARAM_LOB is supported**** - -Testing numeric(38, 16): -****Retrieving numeric(38, 16) as PDO::PARAM_STR is supported**** -****Retrieving numeric(38, 16) as PDO::PARAM_LOB is supported**** - -Testing numeric(38, 38): -Retrieving numeric(38, 38) data as PDO::PARAM_BOOL should return NULL -Retrieving numeric(38, 38) data as PDO::PARAM_INT should return NULL -****Retrieving numeric(38, 38) as PDO::PARAM_STR is supported**** -****Retrieving numeric(38, 38) as PDO::PARAM_LOB is supported**** \ No newline at end of file +Test successfully done \ No newline at end of file From 25f85906b667429b3cd306fc8ddd375257811e25 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Wed, 2 May 2018 12:33:52 -0700 Subject: [PATCH 2/5] Removed the workaround in a decimal test because issue 706 has been fixed --- .../pdo_ae_insert_pdoparam_decimal_precision.phpt | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/test/functional/pdo_sqlsrv/pdo_ae_insert_pdoparam_decimal_precision.phpt b/test/functional/pdo_sqlsrv/pdo_ae_insert_pdoparam_decimal_precision.phpt index 4295c0c2..b179568b 100644 --- a/test/functional/pdo_sqlsrv/pdo_ae_insert_pdoparam_decimal_precision.phpt +++ b/test/functional/pdo_sqlsrv/pdo_ae_insert_pdoparam_decimal_precision.phpt @@ -111,15 +111,7 @@ try { $row = $stmt->fetch(PDO::FETCH_ASSOC); if (abs($row['c_det'] - $inputValues[0]) > $epsilon || abs($row['c_rand'] - $inputValues[1]) > $epsilon) { - // TODO: this is a workaround for the test to pass!!!!! - // with AE, doubles cannot be inserted into a decimal(38, 38) column - // remove the following if block to see the bug - // for more information see VSO task 2723 - if (isAEConnected() && $m1 == 38 && $m2 == 38) { - echo "****Conversion from $pdoParamType to $typeFull is supported****\n"; - } else { - echo "Conversion from $pdoParamType to $typeFull causes data corruption\n"; - } + echo "Conversion from $pdoParamType to $typeFull causes data corruption\n"; } else { echo "****Conversion from $pdoParamType to $typeFull is supported****\n"; } From 31685c42959abbf3809786495e51cf0db778b907 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Wed, 2 May 2018 16:14:08 -0700 Subject: [PATCH 3/5] Added another test for issue 699 --- .../sqlsrv/srv_699_out_param_integer.phpt | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 test/functional/sqlsrv/srv_699_out_param_integer.phpt diff --git a/test/functional/sqlsrv/srv_699_out_param_integer.phpt b/test/functional/sqlsrv/srv_699_out_param_integer.phpt new file mode 100644 index 00000000..f6fd4cb3 --- /dev/null +++ b/test/functional/sqlsrv/srv_699_out_param_integer.phpt @@ -0,0 +1,86 @@ +--TEST-- +GitHub issue #699 - binding integer as output parameter failed +--DESCRIPTION-- +This test uses the sample stored procedure provided by the user, in which an +error situation caused the binding to fail with an irrelevant error message about UTF-8 translation. This test proves that this issue has been fixed. +--ENV-- +PHPT_EXEC=true +--SKIPIF-- + +--FILE-- + "UTF-8", "ConnectionPooling"=>1); +$conn = connect($connectionOptions); + +$tableName1 = "table_issue699_1"; +$tableName2 = "table_issue699_2"; +$procName = "proc_issue699"; + +dropTable($conn, $tableName1); +dropTable($conn, $tableName2); +dropProc($conn, $procName); + +// Create two test tables without encryption +$sql = "CREATE TABLE $tableName1 (correio_electronico NVARCHAR(50), nome NVARCHAR(50), telefones NVARCHAR(15), id_entidade INT)"; +$stmt = sqlsrv_query($conn, $sql); +if (!$stmt) { + fatalError("Failed to create table $tableName1\n"); +} + +$sql = "CREATE TABLE $tableName2 (estado TINYINT NOT NULL DEFAULT 0)"; +$stmt = sqlsrv_query($conn, $sql); +if (!$stmt) { + fatalError("Failed to create table $tableName2\n"); +} + +// Create the stored procedure +$sql = "CREATE PROCEDURE $procName @outparam INT OUTPUT AS + BEGIN + SET @outparam = 100; + INSERT INTO $tableName1 (correio_electronico, nome, telefones, id_entidade) + SELECT 'membros@membros.pt', 'Teste', 'xxx', 1 + FROM $tableName2 CC + WHERE CC.estado = 100 + BEGIN TRY + SET @outparam = 123 + END TRY + BEGIN CATCH + END CATCH + END"; + +$stmt = sqlsrv_query($conn, $sql); +if (!$stmt) { + fatalError("Error in creating the stored procedure $procName\n"); +} + +$sql_callSP = "{call $procName(?)}"; + +// Initialize the output parameter +$outParam = 1; +$params = array(array(&$outParam, SQLSRV_PARAM_OUT)); +$stmt = sqlsrv_query($conn, $sql_callSP, $params); +if (!$stmt) { + fatalError("Error in calling $procName\n"); +} + +while ($res = sqlsrv_next_result($stmt)); + +if ($outParam != 123) { + echo "The output param value $outParam is unexpected!\n"; +} + +dropTable($conn, $tableName1); +dropTable($conn, $tableName2); +dropProc($conn, $procName); + +// Free handles +sqlsrv_free_stmt($stmt); +sqlsrv_close($conn); + +echo "Done\n"; + +?> +--EXPECT-- +Done \ No newline at end of file From 55ae086d43d72a5d3473b6d96b15e6cc955ce430 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Mon, 7 May 2018 16:15:24 -0700 Subject: [PATCH 4/5] Skip test 699 in Linux for now --- test/functional/sqlsrv/srv_699_out_param_integer.phpt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/functional/sqlsrv/srv_699_out_param_integer.phpt b/test/functional/sqlsrv/srv_699_out_param_integer.phpt index f6fd4cb3..7c7c4116 100644 --- a/test/functional/sqlsrv/srv_699_out_param_integer.phpt +++ b/test/functional/sqlsrv/srv_699_out_param_integer.phpt @@ -9,6 +9,14 @@ PHPT_EXEC=true --FILE-- "UTF-8", "ConnectionPooling"=>1); From 39056d907280ed30bbd3301ea2b1be9bc5aed4b4 Mon Sep 17 00:00:00 2001 From: Jenny Tam Date: Tue, 8 May 2018 08:21:54 -0700 Subject: [PATCH 5/5] Removed output param test for now --- .../sqlsrv/srv_699_out_param_integer.phpt | 94 ------------------- 1 file changed, 94 deletions(-) delete mode 100644 test/functional/sqlsrv/srv_699_out_param_integer.phpt diff --git a/test/functional/sqlsrv/srv_699_out_param_integer.phpt b/test/functional/sqlsrv/srv_699_out_param_integer.phpt deleted file mode 100644 index 7c7c4116..00000000 --- a/test/functional/sqlsrv/srv_699_out_param_integer.phpt +++ /dev/null @@ -1,94 +0,0 @@ ---TEST-- -GitHub issue #699 - binding integer as output parameter failed ---DESCRIPTION-- -This test uses the sample stored procedure provided by the user, in which an -error situation caused the binding to fail with an irrelevant error message about UTF-8 translation. This test proves that this issue has been fixed. ---ENV-- -PHPT_EXEC=true ---SKIPIF-- - ---FILE-- - "UTF-8", "ConnectionPooling"=>1); -$conn = connect($connectionOptions); - -$tableName1 = "table_issue699_1"; -$tableName2 = "table_issue699_2"; -$procName = "proc_issue699"; - -dropTable($conn, $tableName1); -dropTable($conn, $tableName2); -dropProc($conn, $procName); - -// Create two test tables without encryption -$sql = "CREATE TABLE $tableName1 (correio_electronico NVARCHAR(50), nome NVARCHAR(50), telefones NVARCHAR(15), id_entidade INT)"; -$stmt = sqlsrv_query($conn, $sql); -if (!$stmt) { - fatalError("Failed to create table $tableName1\n"); -} - -$sql = "CREATE TABLE $tableName2 (estado TINYINT NOT NULL DEFAULT 0)"; -$stmt = sqlsrv_query($conn, $sql); -if (!$stmt) { - fatalError("Failed to create table $tableName2\n"); -} - -// Create the stored procedure -$sql = "CREATE PROCEDURE $procName @outparam INT OUTPUT AS - BEGIN - SET @outparam = 100; - INSERT INTO $tableName1 (correio_electronico, nome, telefones, id_entidade) - SELECT 'membros@membros.pt', 'Teste', 'xxx', 1 - FROM $tableName2 CC - WHERE CC.estado = 100 - BEGIN TRY - SET @outparam = 123 - END TRY - BEGIN CATCH - END CATCH - END"; - -$stmt = sqlsrv_query($conn, $sql); -if (!$stmt) { - fatalError("Error in creating the stored procedure $procName\n"); -} - -$sql_callSP = "{call $procName(?)}"; - -// Initialize the output parameter -$outParam = 1; -$params = array(array(&$outParam, SQLSRV_PARAM_OUT)); -$stmt = sqlsrv_query($conn, $sql_callSP, $params); -if (!$stmt) { - fatalError("Error in calling $procName\n"); -} - -while ($res = sqlsrv_next_result($stmt)); - -if ($outParam != 123) { - echo "The output param value $outParam is unexpected!\n"; -} - -dropTable($conn, $tableName1); -dropTable($conn, $tableName2); -dropProc($conn, $procName); - -// Free handles -sqlsrv_free_stmt($stmt); -sqlsrv_close($conn); - -echo "Done\n"; - -?> ---EXPECT-- -Done \ No newline at end of file