Skip to content

Commit 8ba932b

Browse files
authored
Add new pdo_sqlsrv tests for utf8 encoding errors (#966)
1 parent 486ab9f commit 8ba932b

6 files changed

+672
-0
lines changed
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
--TEST--
2+
Test various connection errors with invalid attributes
3+
--DESCRIPTION--
4+
This is similar to sqlsrv sqlsrv_connStr.phpt such that invalid connection attributes or values used when connecting.
5+
--SKIPIF--
6+
<?php require('skipif_mid-refactor.inc'); ?>
7+
--FILE--
8+
<?php
9+
require_once("MsCommon_mid-refactor.inc");
10+
11+
function invalidEncoding($binary)
12+
{
13+
try {
14+
$options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);
15+
$conn = connect("", $options);
16+
$attr = ($binary) ? PDO::SQLSRV_ENCODING_BINARY : 'gibberish';
17+
18+
$conn->setAttribute(PDO::SQLSRV_ATTR_ENCODING, $attr);
19+
echo "Should have failed about an invalid encoding.\n";
20+
} catch (PDOException $e) {
21+
$error = '*An invalid encoding was specified for SQLSRV_ATTR_ENCODING.';
22+
if (!fnmatch($error, $e->getMessage())) {
23+
echo "invalidEncoding($binary)\n";
24+
var_dump($e->getMessage());
25+
}
26+
}
27+
}
28+
29+
function invalidServer()
30+
{
31+
global $uid, $pwd;
32+
33+
// Test an invalid server name in UTF-8
34+
try {
35+
$options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);
36+
$invalid = pack("H*", "ffc0");
37+
$conn = new PDO("sqlsrv:server = $invalid;", $uid, $pwd, $options);
38+
echo "Should have failed to connect to invalid server.\n";
39+
} catch (PDOException $e) {
40+
$error1 = '*Login timeout expired';
41+
$error2 = '*An error occurred translating the connection string to UTF-16: No mapping for the Unicode character exists in the target multi-byte code page*';
42+
if (fnmatch($error1, $e->getMessage()) || fnmatch($error2, $e->getMessage())) {
43+
; // matched at least one of the expected error messages
44+
} else {
45+
echo "invalidServer\n";
46+
var_dump($e->getMessage());
47+
}
48+
}
49+
}
50+
51+
function utf8APP()
52+
{
53+
global $server, $uid, $pwd;
54+
try {
55+
// Use a UTF-8 name
56+
$app = pack('H*', 'c59ec6a1d0bcc49720c59bc3a4e1839dd180c580e1bb8120ce86c59ac488c4a8c4b02dc5a5e284aec397c5a7');
57+
$dsn = "APP = $app;";
58+
$conn = connect($dsn);
59+
} catch (PDOException $e) {
60+
echo "With APP in UTF8 it should not have failed!\n";
61+
var_dump($e->getMessage());
62+
}
63+
}
64+
65+
function invalidCredentials()
66+
{
67+
global $server, $database;
68+
69+
// Use valid UTF-8
70+
$user = pack('H*', 'c59ec6a1d0bcc49720c59bc3a4e1839dd180c580e1bb8120ce86c59ac488c4a8c4b02dc5a5e284aec397c5a7');
71+
$passwd = pack('H*', 'c59ec6a1d0bcc49720c59bc3a4e1839dd180c580e1bb8120ce86c59ac488c4a8c4b02dc5a5e284aec397c5a7');
72+
73+
$options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);
74+
$error1 = "*Login failed for user \'*\'.";
75+
$error2 = "*Login timeout expired*";
76+
77+
try {
78+
$conn = new PDO("sqlsrv:server = $server; database = $database;", $user, $passwd, $options);
79+
echo "Should have failed to connect\n";
80+
} catch (PDOException $e) {
81+
if (fnmatch($error1, $e->getMessage()) || fnmatch($error2, $e->getMessage())) {
82+
; // matched at least one of the expected error messages
83+
} else {
84+
echo "invalidCredentials()\n";
85+
var_dump($e->getMessage());
86+
}
87+
}
88+
}
89+
90+
function invalidPassword()
91+
{
92+
global $server, $database;
93+
94+
// Use valid UTF-8
95+
$user = pack('H*', 'c59ec6a1d0bcc49720c59bc3a4e1839dd180c580e1bb8120ce86c59ac488c4a8c4b02dc5a5e284aec397c5a7');
96+
// Use invalid UTF-8
97+
$passwd = pack('H*', 'c59ec6c0d0bcc49720c59bc3a4e1839dd180c580e1bb8120ce86c59ac488c4a8c4b02dc5a5e284aec397c5a7');
98+
99+
$options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);
100+
101+
// Possible errors
102+
$error = "*An error occurred translating the connection string to UTF-16: No mapping for the Unicode character exists in the target multi-byte code page.*";
103+
$error1 = "*Login failed for user \'*\'.";
104+
$error2 = "*Login timeout expired*";
105+
106+
try {
107+
$conn = new PDO("sqlsrv:server = $server; database = $database;", $user, $passwd, $options);
108+
echo "Should have failed to connect\n";
109+
} catch (PDOException $e) {
110+
if (!fnmatch($error, $e->getMessage())) {
111+
// Sometimes it might fail with two other possible error messages
112+
if (fnmatch($error1, $e->getMessage()) || fnmatch($error2, $e->getMessage())) {
113+
; // matched at least one of the expected error messages
114+
} else {
115+
echo "invalidPassword()\n";
116+
var_dump($e->getMessage());
117+
}
118+
}
119+
}
120+
}
121+
122+
try {
123+
invalidEncoding(false);
124+
invalidEncoding(true);
125+
invalidServer();
126+
utf8APP();
127+
invalidCredentials();
128+
invalidPassword();
129+
130+
echo "Done\n";
131+
} catch (PDOException $e) {
132+
var_dump($e);
133+
}
134+
?>
135+
--EXPECT--
136+
Done
137+

test/functional/pdo_sqlsrv/pdo_empty_result_error.phpt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ require_once("MsCommon.inc");
1212
// These are the error messages we expect at various points below
1313
$errorNoMoreResults = "There are no more results returned by the query.";
1414
$errorNoFields = "The active result for the query contains no fields.";
15+
$errorNoMoreRows = "There are no more rows in the active result set. Since this result set is not scrollable, no more data may be retrieved.";
1516

1617
// This function compares the expected error message and the error returned by errorInfo().
1718
function CheckError($stmt, $expectedError=NULL)
@@ -94,6 +95,7 @@ echo "Empty result set, call fetch first: ##################################\n";
9495

9596
$stmt = $conn->query("TestEmptySetProc @a='a', @b='w'");
9697
Fetch($stmt);
98+
Fetch($stmt, $errorNoMoreRows);
9799
NextResult($stmt);
98100
Fetch($stmt);
99101
NextResult($stmt, $errorNoMoreResults);
@@ -158,6 +160,7 @@ Next result...
158160
Next result...
159161
Empty result set, call fetch first: ##################################
160162
Fetch...
163+
Fetch...
161164
Next result...
162165
Fetch...
163166
Next result...
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
--TEST--
2+
Test fetching invalid UTF-16 from the server
3+
--DESCRIPTION--
4+
This is similar to sqlsrv 0079.phpt with checking for error conditions concerning encoding issues.
5+
--SKIPIF--
6+
<?php require('skipif_mid-refactor.inc'); ?>
7+
--FILE--
8+
<?php
9+
require_once("MsCommon_mid-refactor.inc");
10+
11+
try {
12+
$conn = connect();
13+
14+
// The following is required or else the insertion would have failed because the input
15+
// was invalid
16+
$conn->setAttribute(PDO::SQLSRV_ATTR_ENCODING, PDO::SQLSRV_ENCODING_SYSTEM);
17+
18+
// Create test table
19+
$tableName = 'pdoUTF16invalid';
20+
$columns = array(new ColumnMeta('int', 'id', 'identity'),
21+
new ColumnMeta('nvarchar(100)', 'c1'));
22+
$stmt = createTable($conn, $tableName, $columns);
23+
24+
// 0xdc00,0xdbff is an invalid surrogate pair
25+
$invalidUTF16 = pack("H*", '410042004300440000DCFFDB45004600');
26+
27+
$insertSql = "INSERT INTO $tableName (c1) VALUES (?)";
28+
$stmt = $conn->prepare($insertSql);
29+
$stmt->bindParam(1, $invalidUTF16, PDO::PARAM_STR, null, PDO::SQLSRV_ENCODING_BINARY);
30+
$stmt->execute();
31+
32+
try {
33+
// Now fetch data with UTF-8 encoding
34+
$tsql = "SELECT * FROM $tableName";
35+
$stmt = $conn->prepare($tsql);
36+
$stmt->setAttribute(PDO::SQLSRV_ATTR_ENCODING, PDO::SQLSRV_ENCODING_UTF8);
37+
$stmt->execute();
38+
$utf8 = $stmt->fetchColumn(1); // Ignore the id column
39+
echo "fetchColumn should have failed with an error.\n";
40+
} catch (PDOException $e) {
41+
$error = '*An error occurred translating string for a field to UTF-8:*';
42+
if ($e->getCode() !== "IMSSP" || !fnmatch($error, $e->getMessage())) {
43+
var_dump($e->getMessage());
44+
}
45+
}
46+
47+
dropProc($conn, 'Utf16InvalidOut');
48+
$createProc = <<<PROC
49+
CREATE PROCEDURE Utf16InvalidOut
50+
@param nvarchar(25) OUTPUT
51+
AS
52+
BEGIN
53+
set @param = convert(nvarchar(25), 0x410042004300440000DCFFDB45004600);
54+
END;
55+
PROC;
56+
57+
$conn->query($createProc);
58+
59+
try {
60+
$invalidUTF16Out = '';
61+
$tsql = '{call Utf16InvalidOut(?)}';
62+
$stmt = $conn->prepare($tsql);
63+
$stmt->setAttribute(PDO::SQLSRV_ATTR_ENCODING, PDO::SQLSRV_ENCODING_UTF8);
64+
$stmt->bindParam(1, $invalidUTF16Out, PDO::PARAM_STR, 25);
65+
$stmt->execute();
66+
} catch (PDOException $e) {
67+
$error = '*An error occurred translating string for an output param to UTF-8:*';
68+
if ($e->getCode() !== "IMSSP" || !fnmatch($error, $e->getMessage())) {
69+
var_dump($e->getMessage());
70+
}
71+
}
72+
73+
echo "Done\n";
74+
75+
// Done testing with the stored procedure and test table
76+
dropProc($conn, 'Utf16InvalidOut');
77+
dropTable($conn, $tableName);
78+
79+
unset($stmt);
80+
unset($conn);
81+
} catch (PDOException $e) {
82+
var_dump($e->errorInfo);
83+
}
84+
?>
85+
--EXPECT--
86+
Done
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
--TEST--
2+
Test inserting UTF-8 stream via PHP including some checking of error conditions
3+
--DESCRIPTION--
4+
This is similar to sqlsrv 0067.phpt with checking for error conditions concerning encoding issues.
5+
--SKIPIF--
6+
<?php require('skipif_mid-refactor.inc'); ?>
7+
--FILE--
8+
<?php
9+
require_once("MsCommon_mid-refactor.inc");
10+
11+
try {
12+
$conn = connect();
13+
14+
// Create test table
15+
$tableName = 'pdoUTF8stream';
16+
$columns = array(new ColumnMeta('tinyint', 'c1'),
17+
new ColumnMeta('char(10)', 'c2'),
18+
new ColumnMeta('float', 'c3'),
19+
new ColumnMeta('varchar(max)', 'c4'));
20+
$stmt = createTable($conn, $tableName, $columns);
21+
22+
$f1 = 1;
23+
$f2 = "testtestte";
24+
$f3 = 12.0;
25+
$f4 = fopen("data://text/plain,This%20is%20some%20text%20meant%20to%20test%20binding%20parameters%20to%20streams", "r");
26+
27+
$insertSql = "INSERT INTO $tableName (c1, c2, c3, c4) VALUES (?, ?, ?, ?)";
28+
$stmt = $conn->prepare($insertSql);
29+
$stmt->bindParam(1, $f1);
30+
$stmt->bindParam(2, $f2);
31+
$stmt->bindParam(3, $f3);
32+
$stmt->bindParam(4, $f4, PDO::PARAM_LOB);
33+
34+
$stmt->execute();
35+
36+
// Next test UTF-8 cutoff in the middle of a valid 3 byte UTF-8 char
37+
$utf8 = str_repeat("41", 8188);
38+
$utf8 = $utf8 . "e38395";
39+
$utf8 = pack("H*", $utf8);
40+
$f4 = fopen("data://text/plain," . $utf8, "r");
41+
$stmt->bindParam(4, $f4, PDO::PARAM_LOB);
42+
$stmt->execute();
43+
44+
// Now test a 2 byte incomplete character
45+
$utf8 = str_repeat("41", 8188);
46+
$utf8 = $utf8 . "dfa0";
47+
$utf8 = pack("H*", $utf8);
48+
$f4 = fopen("data://text/plain," . $utf8, "r");
49+
$stmt->bindParam(4, $f4, PDO::PARAM_LOB);
50+
$stmt->execute();
51+
52+
// Then test a 4 byte incomplete character
53+
$utf8 = str_repeat("41", 8186);
54+
$utf8 = $utf8 . "f1a680bf";
55+
$utf8 = pack("H*", $utf8);
56+
$f4 = fopen("data://text/plain," . $utf8, "r");
57+
$stmt->bindParam(4, $f4, PDO::PARAM_LOB);
58+
$stmt->execute();
59+
60+
// Finally, verify error conditions with invalid inputs
61+
$error = '*An error occurred translating a PHP stream from UTF-8 to UTF-16:*';
62+
63+
// First test UTF-8 cutoff (really cutoff)
64+
$utf8 = str_repeat("41", 8188);
65+
$utf8 = $utf8 . "e383";
66+
$utf8 = pack("H*", $utf8);
67+
$f4 = fopen("data://text/plain," . $utf8, "r");
68+
try {
69+
$stmt->bindParam(4, $f4, PDO::PARAM_LOB);
70+
$stmt->execute();
71+
echo "Should have failed with a cutoff UTF-8 string\n";
72+
} catch (PDOException $e) {
73+
if ($e->getCode() !== "IMSSP" || !fnmatch($error, $e->getMessage())) {
74+
var_dump($e->getMessage());
75+
}
76+
}
77+
78+
// Then test UTF-8 invalid/corrupt stream
79+
$utf8 = str_repeat("41", 8188);
80+
$utf8 = $utf8 . "e38395e38395";
81+
$utf8 = substr_replace($utf8, "fe", 1000, 2);
82+
$utf8 = pack("H*", $utf8);
83+
$f4 = fopen("data://text/plain," . $utf8, "r");
84+
try {
85+
$stmt->bindParam(4, $f4, PDO::PARAM_LOB);
86+
$stmt->execute();
87+
echo "Should have failed with an invalid UTF-8 string\n";
88+
} catch (PDOException $e) {
89+
if ($e->getCode() !== "IMSSP" || !fnmatch($error, $e->getMessage())) {
90+
var_dump($e->getMessage());
91+
}
92+
}
93+
94+
echo "Done\n";
95+
96+
// Done testing with stored procedures and table
97+
dropTable($conn, $tableName);
98+
99+
unset($stmt);
100+
unset($conn);
101+
} catch (PDOException $e) {
102+
var_dump($e->errorInfo);
103+
}
104+
?>
105+
--EXPECT--
106+
Done

0 commit comments

Comments
 (0)