diff --git a/ext/pdo_odbc/odbc_stmt.c b/ext/pdo_odbc/odbc_stmt.c index bd4a2f6162d09..af0fc46b0ccdb 100644 --- a/ext/pdo_odbc/odbc_stmt.c +++ b/ext/pdo_odbc/odbc_stmt.c @@ -607,6 +607,19 @@ static int odbc_stmt_describe(pdo_stmt_t *stmt, int colno) } } colsize = displaysize; + if ( S->cols[colno].coltype == SQL_WCHAR +#ifdef SQL_WVARCHAR + || S->cols[colno].coltype == SQL_WVARCHAR +#endif +#ifdef SQL_WLONGVARCHAR + || S->cols[colno].coltype == SQL_WLONGVARCHAR +#endif + ) { + /* displaysize is counted by characters; + for unicode, each could take up to 4 bytes in UTF-8; + see https://www.rfc-editor.org/rfc/rfc3629 */ + colsize = displaysize * 4; + } col->maxlen = S->cols[colno].datalen = colsize; col->name = zend_string_init(S->cols[colno].colname, colnamelen, 0); diff --git a/ext/pdo_odbc/tests/gh9498_1.phpt b/ext/pdo_odbc/tests/gh9498_1.phpt new file mode 100644 index 0000000000000..512f114d81d37 --- /dev/null +++ b/ext/pdo_odbc/tests/gh9498_1.phpt @@ -0,0 +1,87 @@ +--TEST-- +Bug GH-9498 (unicode string corruption during SELECT) +--EXTENSIONS-- +pdo_odbc +--SKIPIF-- + +--FILE-- +setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); + +$tests = [ + [ + "name" => "beamed_eighth_notes_binary_encoded_utf16le", + "expr" => "CAST(0x6b26 AS nvarchar(1))", + "expected_answer" => "♫", + ], + [ + "name" => "beamed_eighth_notes_nchar", + "expr" => "NCHAR(0x266b)", + "expected_answer" => "♫", + ], + [ + "name" => "beamed_eighth_notes_u+266b_constant", + "expr" => "N'♫'", + "expected_answer" => "♫", + ], + [ + "name" => "ligature_fi_nchar", + "expr" => "NCHAR(0xfb01)", + "expected_answer" => "fi", + ], + [ + "name" => "php_ascii_string_constant", + "expr" => "'php'", + "expected_answer" => "php", + ], + [ + "name" => "php_unicode_string_constant", + "expr" => "N'php'", + "expected_answer" => "php", + ], + [ + "name" => "pink_in_czech_binary_encoded_utf16le", + "expr" => "CAST(0x72006f017e016f007600fd00 AS nvarchar(6))", + "expected_answer" => "růžový", + ], + [ + "name" => "pink_in_czech_unicode_string_constant", + "expr" => "N'růžový'", + "expected_answer" => "růžový", + ], + [ + "name" => "segno_u+1d10b_constant", + "expr" => "N'𝄋'", + "expected_answer" => "𝄋", + ], +]; + +foreach ($tests as $t) { + print $t["name"] . ": "; + $sql = "SELECT " . $t["expr"] . ' AS "' . $t["name"] . '";'; + $results = $db->query($sql, \PDO::FETCH_NUM)->fetch(); + if ( $results[0] == $t["expected_answer"] ) { + print "PASS"; + } else { + print "FAIL!"; + print "\tresult: '" . $results[0] . "'"; + print "\texpected: '" . $t["expected_answer"] . "'"; + } + print "\n"; +} +?> +--EXPECT-- +beamed_eighth_notes_binary_encoded_utf16le: PASS +beamed_eighth_notes_nchar: PASS +beamed_eighth_notes_u+266b_constant: PASS +ligature_fi_nchar: PASS +php_ascii_string_constant: PASS +php_unicode_string_constant: PASS +pink_in_czech_binary_encoded_utf16le: PASS +pink_in_czech_unicode_string_constant: PASS +segno_u+1d10b_constant: PASS