Merge branch 'dev' into codecov

This commit is contained in:
Jenny Tam 2017-11-21 16:33:38 -08:00
commit 33b4922921
19 changed files with 758 additions and 404 deletions

View file

@ -3,6 +3,34 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
## Windows/Linux/macOS 5.1.2-preview - 2017-11-21
Updated PECL release packages. Here is the list of updates:
### Fixed
- Support for non-UTF8 locales in Linux and macOS
- Fixed crash caused by executing an invalid query in a transaction (Issue [#434](https://github.com/Microsoft/msphpsql/issues/434))
- Fixed regression in sqlsrv_next_result returning a no fields error when the active result set is null (Issue [#581](https://github.com/Microsoft/msphpsql/issues/581))
- Fixed incorrect active result set when sqlsrv_next_result or PDOStatement::nextRowset is called when Column Encryption is enabled (Issue [#574](https://github.com/Microsoft/msphpsql/issues/574))
- Fixed data corruption in fetching from an encrypted max column after calling sqlsrv_next_result or PDOStatemet::nextRowset (Issue [#580](https://github.com/Microsoft/msphpsql/issues/580))
- Added error handling for using PDO::SQLSRV_ATTR_DIRECT_QUERY or PDO::ATTR_EMULATE_PREPARES in a Column Encryption enabled connection
- Added error handling for binding TEXT, NTEXT or IMAGE as output parameter (Issue [#231](https://github.com/Microsoft/msphpsql/issues/231))
### Limitations
- In Linux and macOS, setlocale() only takes effect if it is invoked before the first connection. The subsequent locale setting will not work
- Always Encrypted functionalities are only supported using [MSODBC 17 preview](https://github.com/Microsoft/msphpsql/tree/dev/ODBC%2017%20binaries%20preview)
- ODBC binaries for macOS available upon request
- MSODBC 17 preview msodbcsql.msi only works in Windows10
- [Always Encrypted limitations](https://github.com/Microsoft/msphpsql/wiki/Features#aelimitation)
- When using sqlsrv_query with Always Encrypted feature, SQL type has to be specified for each input (see [here](https://github.com/Microsoft/msphpsql/wiki/Features#aebindparam))
- No support for inout / output params when using sql_variant type
### Known Issues
- Binding decimal input as a string when Column Encryption is enabled may change the precision of the input
- Connection pooling on Linux doesn't work properly when using the MSODBC17 preview
- When pooling is enabled in Linux or macOS
- unixODBC <= 2.3.4 (Linux and macOS) might not return proper diagnostics information, such as error messages, warnings and informative messages
- due to this unixODBC bug, fetch large data (such as xml, binary) as streams as a workaround. See the examples [here](https://github.com/Microsoft/msphpsql/wiki/Connection-Pooling-on-Linux-and-Mac)
## Windows/Linux 5.1.1-preview - 2017-10-20
Updated PECL release packages. Here is the list of updates:

View file

@ -9,6 +9,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && apt-get update && \
apt-utils \
autoconf \
curl \
libcurl3 \
g++ \
gcc \
git \
@ -28,17 +29,14 @@ ENV TEST_PHP_SQL_SERVER sql
ENV TEST_PHP_SQL_UID sa
ENV TEST_PHP_SQL_PWD Password123
# add locale iso-8859-1
RUN sed -i 's/# en_US ISO-8859-1/en_US ISO-8859-1/g' /etc/locale.gen
RUN locale-gen en_US
# set locale to utf-8
RUN locale-gen en_US.UTF-8
ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en' LC_ALL='en_US.UTF-8'
#install ODBC driver
RUN curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add -
RUN curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list > /etc/apt/sources.list.d/mssql-release.list
RUN export DEBIAN_FRONTEND=noninteractive && apt-get update && ACCEPT_EULA=Y apt-get install -y msodbcsql mssql-tools
ENV PATH="/opt/mssql-tools/bin:${PATH}"
#install coveralls
RUN pip install --upgrade pip && pip install cpp-coveralls
@ -47,8 +45,14 @@ RUN pip install --upgrade pip && pip install cpp-coveralls
#another option is to copy source to build directory on image
RUN mkdir -p $PHPSQLDIR
COPY . $PHPSQLDIR
WORKDIR $PHPSQLDIR/source/
#install ODBC 17 preview driver
WORKDIR $PHPSQLDIR
RUN export DEBIAN_FRONTEND=noninteractive && apt-get update && ACCEPT_EULA=Y dpkg -i "./ODBC 17 binaries preview/Ubuntu 16/msodbcsql_17.0.0.1-1_amd64.deb"
RUN export DEBIAN_FRONTEND=noninteractive && apt-get update && ACCEPT_EULA=Y dpkg -i "./ODBC 17 binaries preview/Ubuntu 16/mssql-tools_17.0.0.1-1_amd64.deb"
ENV PATH="/opt/mssql-tools/bin:${PATH}"
WORKDIR $PHPSQLDIR/source/
RUN chmod +x ./packagize.sh
RUN /bin/bash -c "./packagize.sh"

View file

@ -1067,33 +1067,6 @@ int pdo_sqlsrv_stmt_next_rowset( _Inout_ pdo_stmt_t *stmt TSRMLS_DC )
SQLSRV_ASSERT( driver_stmt != NULL, "pdo_sqlsrv_stmt_next_rowset: driver_data object was null" );
// Return the correct error in case the user calls nextRowset() on a null result set.
// Null means that SQLNumResultCols() returns 0 and SQLRowCount does not return > 0. But first
// check that the statement has been executed and that we are not past the end of a non-null
// result set to make sure the user gets the correct error message. These checks are also
// done in core_sqlsrv_next_result(), but we cannot check for null results there because that
// function can be called without calling this one, and SQLSRV_ERROR_NO_FIELDS can then
// be triggered incorrectly.
CHECK_CUSTOM_ERROR( !driver_stmt->executed, driver_stmt, SQLSRV_ERROR_STATEMENT_NOT_EXECUTED ) {
throw core::CoreException();
}
CHECK_CUSTOM_ERROR( driver_stmt->past_next_result_end, driver_stmt, SQLSRV_ERROR_NEXT_RESULT_PAST_END ) {
throw core::CoreException();
}
// Now make sure the result set is not null.
bool has_result = core_sqlsrv_has_any_result( driver_stmt );
// Note that if fetch_called is false but has_result is true (i.e. the user is calling
// nextRowset() on a non-null result set before calling fetch()), it is handled
// in core_sqlsrv_next_result() below.
if( !driver_stmt->fetch_called ) {
CHECK_CUSTOM_ERROR( !has_result, driver_stmt, SQLSRV_ERROR_NO_FIELDS ) {
throw core::CoreException();
}
}
core_sqlsrv_next_result( static_cast<sqlsrv_stmt*>( stmt->driver_data ) TSRMLS_CC );
// clear the current meta data since the new result will generate new meta data

View file

@ -39,6 +39,12 @@ void core_sqlsrv_minit( _Outptr_ sqlsrv_context** henv_cp, _Inout_ sqlsrv_contex
SQLSRV_STATIC_ASSERT( sizeof( sqlsrv_sqltype ) == sizeof( zend_long ) );
SQLSRV_STATIC_ASSERT( sizeof( sqlsrv_phptype ) == sizeof( zend_long ));
#ifndef _WIN32
// set locale from environment
// this is necessary for ODBC and MUST be done before connection
setlocale(LC_ALL, "");
#endif
*henv_cp = *henv_ncp = SQL_NULL_HANDLE; // initialize return values to NULL
try {

View file

@ -27,6 +27,17 @@
#include <locale>
#define CP_UTF8 65001
#define CP_ISO8859_1 28591
#define CP_ISO8859_2 28592
#define CP_ISO8859_3 28593
#define CP_ISO8859_4 28594
#define CP_ISO8859_5 28595
#define CP_ISO8859_6 28596
#define CP_ISO8859_7 28597
#define CP_ISO8859_8 28598
#define CP_ISO8859_9 28599
#define CP_ISO8859_13 28603
#define CP_ISO8859_15 28605
#define CP_UTF16 1200
#define CP_ACP 0 // default to ANSI code page
@ -178,14 +189,15 @@ private:
SystemLocale & operator=( const SystemLocale & );
std::locale * m_pLocale;
UINT m_uAnsiCP;
explicit SystemLocale( const char * localeName );
~SystemLocale();
static UINT ExpandSpecialCP( UINT codepage )
{
// Convert CP_ACP, CP_OEM to CP_UTF8
return (codepage < 2 ? CP_UTF8 : codepage);
// skip SQLSRV_ENCODING_CHAR
return (codepage <= 3 ? Singleton().m_uAnsiCP : codepage);
}
// Returns the number of bytes this UTF8 code point expects
@ -217,7 +229,7 @@ private:
inline UINT SystemLocale::AnsiCP() const
{
return CP_UTF8;
return m_uAnsiCP;
}
inline UINT SystemLocale::MaxCharCchSize( UINT codepage )

View file

@ -1,5 +1,5 @@
//---------------------------------------------------------------------------------------------------------------------------------
// File: LocalizationImpl.hpp
// File: localizationimpl.cpp
//
// Contents: Contains non-inline code for the SystemLocale class
// Must be included in one c/cpp file per binary
@ -71,6 +71,17 @@ const cp_iconv cp_iconv::g_cp_iconv[] = {
{ 1256, "CP1256//TRANSLIT" },
{ 1257, "CP1257//TRANSLIT" },
{ 1258, "CP1258//TRANSLIT" },
{ CP_ISO8859_1, "ISO8859-1//TRANSLIT" },
{ CP_ISO8859_2, "ISO8859-2//TRANSLIT" },
{ CP_ISO8859_3, "ISO8859-3//TRANSLIT" },
{ CP_ISO8859_4, "ISO8859-4//TRANSLIT" },
{ CP_ISO8859_5, "ISO8859-5//TRANSLIT" },
{ CP_ISO8859_6, "ISO8859-6//TRANSLIT" },
{ CP_ISO8859_7, "ISO8859-7//TRANSLIT" },
{ CP_ISO8859_8, "ISO8859-8//TRANSLIT" },
{ CP_ISO8859_9, "ISO8859-9//TRANSLIT" },
{ CP_ISO8859_13, "ISO8859-13//TRANSLIT" },
{ CP_ISO8859_15, "ISO8859-15//TRANSLIT" },
{ 12000, "UTF-32LE" }
};
const size_t cp_iconv::g_cp_iconv_count = ARRAYSIZE(cp_iconv::g_cp_iconv);
@ -270,7 +281,42 @@ using namespace std;
SystemLocale::SystemLocale( const char * localeName )
: m_pLocale( new std::locale(localeName) )
, m_uAnsiCP(CP_UTF8)
{
struct LocaleCP
{
const char* localeName;
UINT codePage;
};
#define CPxxx(cp) { "CP" #cp, cp }
#define ISO8859(n) { "ISO-8859-" #n, CP_ISO8859_ ## n }, \
{ "8859_" #n, CP_ISO8859_ ## n }, \
{ "ISO8859-" #n, CP_ISO8859_ ## n }, \
{ "ISO8859" #n, CP_ISO8859_ ## n }, \
{ "ISO_8859-" #n, CP_ISO8859_ ## n }, \
{ "ISO_8859_" #n, CP_ISO8859_ ## n }
const LocaleCP lcpTable[] = {
{ "utf8", CP_UTF8 },
{ "UTF-8", CP_UTF8 },
CPxxx(1252), CPxxx(850), CPxxx(437), CPxxx(874), CPxxx(932), CPxxx(936), CPxxx(949), CPxxx(950),
CPxxx(1250), CPxxx(1251), CPxxx(1253), CPxxx(1254), CPxxx(1255), CPxxx(1256), CPxxx(1257), CPxxx(1258),
ISO8859(1), ISO8859(2), ISO8859(3), ISO8859(4), ISO8859(5), ISO8859(6),
ISO8859(7), ISO8859(8), ISO8859(9), ISO8859(13), ISO8859(15),
{ "UTF-32LE", 12000 }
};
if (localeName)
{
const char *charsetName = strchr(localeName, '.');
charsetName = charsetName ? charsetName + 1 : localeName;
for (const LocaleCP& lcp : lcpTable)
{
if (!strncasecmp(lcp.localeName, charsetName, strlen(lcp.localeName)))
{
m_uAnsiCP = lcp.codePage;
return;
}
}
}
}
SystemLocale::~SystemLocale()
@ -285,7 +331,8 @@ const SystemLocale & SystemLocale::Singleton()
#if !defined(__GNUC__) || defined(NO_THREADSAFE_STATICS)
#error "Relying on GCC's threadsafe initialization of local statics."
#endif
static const SystemLocale s_Default( "en_US.utf-8" );
// get locale from environment and set as default
static const SystemLocale s_Default(setlocale(LC_ALL, NULL));
return s_Default;
}

View file

@ -28,7 +28,7 @@
// Increase Patch for backward compatible fixes.
#define SQLVERSION_MAJOR 5
#define SQLVERSION_MINOR 1
#define SQLVERSION_PATCH 1
#define SQLVERSION_PATCH 2
#define SQLVERSION_BUILD 0
// Semantic versioning pre-release

View file

@ -561,32 +561,6 @@ PHP_FUNCTION( sqlsrv_next_result )
try {
// Return the correct error in case the user calls sqlsrv_next_result() on a null result set.
// Null means that SQLNumResultCols() returns 0 and SQLRowCount does not return > 0. But first
// check that the statement has been executed and that we are not past the end of a non-null
// result set to make sure the user gets the correct error message. These checks are also
// done in core_sqlsrv_next_result(), but we cannot check for null results there because that
// function can be called without calling this one, and SQLSRV_ERROR_NO_FIELDS can then
// be triggered incorrectly.
CHECK_CUSTOM_ERROR( !stmt->executed, stmt, SQLSRV_ERROR_STATEMENT_NOT_EXECUTED ) {
throw core::CoreException();
}
CHECK_CUSTOM_ERROR( stmt->past_next_result_end, stmt, SQLSRV_ERROR_NEXT_RESULT_PAST_END ) {
throw core::CoreException();
}
bool has_result = core_sqlsrv_has_any_result( stmt );
// Note that if fetch_called is false but has_result is true (i.e. the user is calling
// sqlsrv_next_result() on a non-null result set before calling fetch()), it is handled
// in core_sqlsrv_next_result() below.
if( !stmt->fetch_called ) {
CHECK_CUSTOM_ERROR( !has_result, stmt, SQLSRV_ERROR_NO_FIELDS ) {
throw core::CoreException();
}
}
core_sqlsrv_next_result( stmt TSRMLS_CC, true );
if( stmt->past_next_result_end ) {

View file

@ -1,7 +1,7 @@
--TEST--
Error messages from null result sets
Error messages from nonempty, empty, and null result sets
--DESCRIPTION--
Test that calling nextRowset() on an empty result set produces the correct error message. Fix for Github 507.
Test that calling nextRowset() and fetching on nonempty, empty, and null result sets produces the correct results or error messages.
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
@ -9,92 +9,171 @@ Test that calling nextRowset() on an empty result set produces the correct error
require_once("MsSetup.inc");
require_once("MsCommon.inc");
// These are the error messages we expect at various points below
$errorNoMoreResults = "There are no more results returned by the query.";
$errorNoFields = "The active result for the query contains no fields.";
// This function compares the expected error message and the error returned by errorInfo().
function CheckError($stmt, $expectedError=NULL)
{
$actualError = $stmt->errorInfo();
if ($actualError[2] != $expectedError) {
echo "Wrong error message:\n";
print_r($actualError);
}
}
function Fetch($stmt, $error=NULL)
{
echo "Fetch...\n";
$result = $stmt->fetchObject();
print_r($result);
CheckError($stmt, $error);
}
function NextResult($stmt, $error=NULL)
{
echo "Next result...\n";
$stmt->nextRowset();
CheckError($stmt, $error);
}
$conn = new PDO( "sqlsrv:Server = $server; Database = $databaseName; ", $uid, $pwd );
$conn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
$conn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
DropTable($conn, 'TestEmptySetTable');
$stmt = $conn->query("CREATE TABLE TestEmptySetTable ([c1] nvarchar(10),[c2] nvarchar(10))");
$stmt = $conn->query("INSERT INTO TestEmptySetTable (c1, c2) VALUES ('a', 'b')");
// Create a procedure that can return a result set or can return nothing
// Create a procedure that can return a nonempty result set, an empty result set, or a null result
DropProc($conn, 'TestEmptySetProc');
$stmt = $conn->query("CREATE PROCEDURE TestEmptySetProc @a nvarchar(10), @b nvarchar(10)
AS SET NOCOUNT ON
BEGIN
IF @b='b'
AS SET NOCOUNT ON
BEGIN
SELECT 'a' as testValue
END
ELSE
BEGIN
UPDATE TestEmptySetTable SET c2 = 'c' WHERE c1 = @a
END
END");
IF @b='b'
BEGIN
SELECT 'a' as testValue
END
ELSE IF @b='w'
BEGIN
SELECT * FROM TestEmptySetTable WHERE c1 = @b
END
ELSE
BEGIN
UPDATE TestEmptySetTable SET c2 = 'c' WHERE c1 = @a
END
END");
// errors out when reaching the second nextRowset() call
// returned error indicates there are no more results
echo "Return a nonempty result set:\n";
try
{
$stmt = $conn->query("TestEmptySetProc @a='a', @b='b'");
$result = $stmt->fetchAll();
print_r($result);
$stmt->nextRowset();
$result = $stmt->fetchAll();
print_r($result);
$stmt->nextRowset();
}
catch(Exception $e)
{
echo $e->getMessage()."\n";
}
// Call fetch on a nonempty result set
echo "Nonempty result set, call fetch first: ###############################\n";
// errors out indicating the result set contains no fields
echo "Return an empty result set, call nextRowset on it before fetching anything:\n";
try
{
$stmt = $conn->query("TestEmptySetProc @a='a', @b='c'");
$stmt->nextRowset();
}
catch(Exception $e)
{
echo $e->getMessage()."\n";
}
$stmt = $conn->query("TestEmptySetProc @a='a', @b='b'");
Fetch($stmt);
NextResult($stmt);
Fetch($stmt);
NextResult($stmt, $errorNoMoreResults);
// errors out indicating the result set contains no fields
echo "Return an empty result set, call fetch on it:\n";
try
{
$stmt = $conn->query("TestEmptySetProc @a='a', @b='c'");
$result = $stmt->fetchAll();
print_r($result);
}
catch(Exception $e)
{
echo $e->getMessage()."\n";
}
// Call nextRowset on a nonempty result set
echo "Nonempty result set, call nextRowset first: #########################\n";
$stmt = $conn->query("TestEmptySetProc @a='a', @b='b'");
NextResult($stmt);
Fetch($stmt);
NextResult($stmt, $errorNoMoreResults);
// Call nextRowset twice in succession on a nonempty result set
echo "Nonempty result set, call nextRowset twice: #########################\n";
$stmt = $conn->query("TestEmptySetProc @a='a', @b='b'");
NextResult($stmt);
NextResult($stmt, $errorNoMoreResults);
// Call fetch on an empty result set
echo "Empty result set, call fetch first: ##################################\n";
$stmt = $conn->query("TestEmptySetProc @a='a', @b='w'");
Fetch($stmt);
NextResult($stmt);
Fetch($stmt);
NextResult($stmt, $errorNoMoreResults);
// Call nextRowset on an empty result set
echo "Empty result set, call nextRowset first: ############################\n";
$stmt = $conn->query("TestEmptySetProc @a='a', @b='w'");
NextResult($stmt);
Fetch($stmt);
NextResult($stmt, $errorNoMoreResults);
// Call nextRowset twice in succession on an empty result set
echo "Empty result set, call nextRowset twice: ############################\n";
$stmt = $conn->query("TestEmptySetProc @a='a', @b='w'");
NextResult($stmt);
NextResult($stmt, $errorNoMoreResults);
// Call fetch on a null result set
echo "Null result set, call fetch first: ###################################\n";
$stmt = $conn->query("TestEmptySetProc @a='a', @b='c'");
Fetch($stmt, $errorNoFields);
NextResult($stmt);
// Call nextRowset on a null result set
echo "Null result set, call next result first: #############################\n";
$stmt = $conn->query("TestEmptySetProc @a='a', @b='c'");
NextResult($stmt);
Fetch($stmt);
// Call nextRowset twice in succession on a null result set
echo "Null result set, call next result twice: #############################\n";
$stmt = $conn->query("TestEmptySetProc @a='a', @b='c'");
NextResult($stmt);
NextResult($stmt, $errorNoMoreResults);
$stmt = $conn->query("DROP TABLE TestEmptySetTable");
$stmt = $conn->query("DROP PROCEDURE TestEmptySetProc");
$stmt = null;
$conn = null;
?>
--EXPECT--
Return a nonempty result set:
Array
Nonempty result set, call fetch first: ###############################
Fetch...
stdClass Object
(
[0] => Array
(
[testValue] => a
[0] => a
)
[testValue] => a
)
Array
(
)
SQLSTATE[IMSSP]: There are no more results returned by the query.
Return an empty result set, call nextRowset on it before fetching anything:
SQLSTATE[IMSSP]: The active result for the query contains no fields.
Return an empty result set, call fetch on it:
SQLSTATE[IMSSP]: The active result for the query contains no fields.
Next result...
Fetch...
Next result...
Nonempty result set, call nextRowset first: #########################
Next result...
Fetch...
Next result...
Nonempty result set, call nextRowset twice: #########################
Next result...
Next result...
Empty result set, call fetch first: ##################################
Fetch...
Next result...
Fetch...
Next result...
Empty result set, call nextRowset first: ############################
Next result...
Fetch...
Next result...
Empty result set, call nextRowset twice: ############################
Next result...
Next result...
Null result set, call fetch first: ###################################
Fetch...
Next result...
Null result set, call next result first: #############################
Next result...
Fetch...
Null result set, call next result twice: #############################
Next result...
Next result...

View file

@ -454,4 +454,15 @@ function handleErrors()
}
}
// non-UTF8 locale support in ODBC 17 and above only
function isLocaleSupported()
{
$conn = AE\connect();
$msodbcsql_ver = sqlsrv_client_info($conn)['DriverVer'];
if (explode(".", $msodbcsql_ver)[0] < 17) {
return false;
}
}
?>

View file

@ -9,7 +9,7 @@
*/
function getTestData($index)
function getTestData_UTF8($index)
{
$inputs = null;
switch ($index)

View file

@ -732,12 +732,13 @@ function getInsertArray($index)
{
if (! UseUTF8data()) {
require_once('MsData.inc');
return getTestData($index);
} else {
require_once('MsData_UTF8.inc');
return getTestData_UTF8($index);
}
// get array of input values
return getTestData($index);
}
/**

View file

@ -1,9 +1,14 @@
--TEST--
Fetch Field Data Test verifies the data retrieved via "sqlsrv_get_field"
Fetch Field Data Test verifies the data retrieved via sqlsrv_get_field
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_versions_old.inc'); ?>
<?
// locale must be set before 1st connection
if ( !isWindows() ) {
setlocale(LC_ALL, "en_US.ISO-8859-1");
}
?>
--FILE--
<?php
@ -11,13 +16,15 @@ require_once('MsCommon.inc');
function fetchFields()
{
$testName = "Fetch - Field Data";
startTest($testName);
setup();
$tableName = 'TC43test';
$conn1 = AE\connect();
if (useUTF8Data()) {
$conn1 = AE\connect(array('CharacterSet'=>'UTF-8'));
} else {
$conn1 = AE\connect();
}
AE\createTestTable($conn1, $tableName);
$startRow = 1;
@ -62,8 +69,6 @@ function fetchFields()
dropTable($conn1, $tableName);
sqlsrv_close($conn1);
endTest($testName);
}
function checkData($col, $actual, $expected)
@ -91,16 +96,36 @@ function checkData($col, $actual, $expected)
return ($success);
}
if (! isWindows()) {
setUTF8Data(true);
if (!isWindows()) {
setlocale(LC_ALL, "en_US.ISO-8859-1");
}
$testName = "Fetch - Field Data";
// test ansi only if windows or non-UTF8 locales are supported (ODBC 17 and above)
startTest($testName);
if (isWindows() || isLocaleSupported()) {
try {
setUTF8Data(false);
fetchFields();
} catch (Exception $e) {
echo $e->getMessage();
}
}
endTest($testName);
// test utf8
startTest($testName);
try {
setUTF8Data(true);
fetchFields();
} catch (Exception $e) {
echo $e->getMessage();
}
setUTF8Data(false);
endTest($testName);
?>
--EXPECT--
Test "Fetch - Field Data" completed successfully.
Test "Fetch - Field Data" completed successfully.

View file

@ -6,24 +6,25 @@ by checking all fetch type modes.
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_versions_old.inc'); ?>
<?
// locale must be set before 1st connection
if ( !isWindows() ) {
setlocale(LC_ALL, "en_US.ISO-8859-1");
}
?>
--FILE--
<?php
require_once('MsCommon.inc');
function fetchRow($minFetchMode, $maxFetchMode)
{
$testName = "Fetch - Array";
startTest($testName);
if (!isMarsSupported()) {
endTest($testName);
return;
}
setup();
$tableName = 'TC44test';
if (!isWindows()) {
if (useUTF8Data()) {
$conn1 = AE\connect(array( 'CharacterSet'=>'UTF-8' ));
} else {
$conn1 = AE\connect();
@ -43,7 +44,6 @@ function fetchRow($minFetchMode, $maxFetchMode)
} else {
$count = sqlsrv_num_fields($stmt1);
if ($count != $numFields) {
setUTF8Data(false);
die("Unexpected number of fields: $count");
}
}
@ -73,8 +73,6 @@ function fetchRow($minFetchMode, $maxFetchMode)
dropTable($conn1, $tableName);
sqlsrv_close($conn1);
endTest($testName);
}
function fetchArray($stmt, $stmtRef, $mode, $rows, $fields)
@ -104,13 +102,11 @@ function fetchArray($stmt, $stmtRef, $mode, $rows, $fields)
}
$rowSize = count($row);
if ($rowSize != $size) {
setUTF8Data(false);
die("Row array has an incorrect size: ".$rowSize);
}
$rowRref = sqlsrv_fetch($stmtRef);
for ($j = 0; $j < $fields; $j++) {
if (!checkData($row, $stmtRef, $j, $fetchMode)) {
setUTF8Data(false);
die("Data corruption on row ".($i + 1)." column ".($j + 1));
}
}
@ -155,17 +151,37 @@ function checkData($row, $stmt, $index, $mode)
return ($success);
}
// locale must be set before 1st connection
if (!isWindows()) {
setUTF8Data(true);
setlocale(LC_ALL, "en_US.ISO-8859-1");
}
global $testName;
$testName = "Fetch - Array";
// test ansi only if windows or non-UTF8 locales are supported (ODBC 17 and above)
startTest($testName);
if (isWindows() || isLocaleSupported()) {
try {
setUTF8Data(false);
fetchRow(1, 4);
} catch (Exception $e) {
echo $e->getMessage();
}
}
endTest($testName);
// test utf8
startTest($testName);
try {
setUTF8Data(true);
fetchRow(1, 4);
} catch (Exception $e) {
echo $e->getMessage();
}
setUTF8Data(false);
endTest($testName);
?>
--EXPECT--
Test "Fetch - Array" completed successfully.
Test "Fetch - Array" completed successfully.

View file

@ -5,24 +5,29 @@ Verifies the functionality of "sqlsrv_next_result"
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_versions_old.inc'); ?>
<?
// locale must be set before 1st connection
if ( !isWindows() ) {
setlocale(LC_ALL, "en_US.ISO-8859-1");
}
?>
--FILE--
<?php
require_once('MsCommon.inc');
function fetchFields()
{
$testName = "Fetch - Next Result";
startTest($testName);
if (!IsMarsSupported()) {
endTest($testName);
return;
}
setup();
$tableName = 'TC46test';
$conn1 = AE\connect();
if (useUTF8Data()) {
$conn1 = AE\connect(array('CharacterSet'=>'UTF-8'));
} else {
$conn1 = AE\connect();
}
AE\createTestTable($conn1, $tableName);
$noRows = 10;
@ -77,21 +82,39 @@ function fetchFields()
dropTable($conn1, $tableName);
sqlsrv_close($conn1);
endTest($testName);
}
// locale must be set before 1st connection
if (!isWindows()) {
setUTF8Data(true);
setlocale(LC_ALL, "en_US.ISO-8859-1");
}
global $testName;
$testName = "Fetch - Next Result";
// test ansi only if windows or non-UTF8 locales are supported (ODBC 17 and above)
startTest($testName);
if (isWindows() || isLocaleSupported()) {
try {
setUTF8Data(false);
fetchFields();
} catch (Exception $e) {
echo $e->getMessage();
}
}
endTest($testName);
// test utf8
startTest($testName);
try {
setUTF8Data(true);
fetchFields();
} catch (Exception $e) {
echo $e->getMessage();
}
setUTF8Data(false);
endTest($testName);
?>
--EXPECT--
Test "Fetch - Next Result" completed successfully.
Test "Fetch - Next Result" completed successfully.

View file

@ -6,19 +6,20 @@ can be successfully retrieved as streams.
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_versions_old.inc'); ?>
<?// locale must be set before 1st connection
if ( !isWindows() ) {
setlocale(LC_ALL, "en_US.ISO-8859-1");
}
?>
--FILE--
<?php
require_once('MsCommon.inc');
function streamRead($noRows, $startRow)
{
$testName = "Stream - Read";
startTest($testName);
setup();
$tableName = 'TC51test';
if (! isWindows()) {
if (useUTF8Data()) {
$conn1 = AE\connect(array( 'CharacterSet'=>'UTF-8' ));
} else {
$conn1 = AE\connect();
@ -51,8 +52,6 @@ function streamRead($noRows, $startRow)
dropTable($conn1, $tableName);
sqlsrv_close($conn1);
endTest($testName);
}
function verifyStream($stmt, $row, $colIndex)
@ -80,7 +79,6 @@ function verifyStream($stmt, $row, $colIndex)
fclose($stream);
$data = AE\getInsertData($row, $col);
if (!checkData($col, $value, $data)) {
setUTF8Data(false);
trace("Data corruption on row $row column $col\n");
die("Data corruption on row $row column $col\n");
}
@ -117,18 +115,37 @@ function checkData($col, $actual, $expected)
return ($success);
}
if (! isWindows()) {
setUTF8Data(true);
// locale must be set before 1st connection
if (!isWindows()) {
setlocale(LC_ALL, "en_US.ISO-8859-1");
}
global $testName;
$testName = "Stream - Read";
// test ansi only if windows or non-UTF8 locales are supported (ODBC 17 and above)
startTest($testName);
if (isWindows() || isLocaleSupported()) {
try {
setUTF8Data(false);
streamRead(20, 1);
} catch (Exception $e) {
echo $e->getMessage();
}
}
endTest($testName);
// test utf8
startTest($testName);
try {
setUTF8Data(true);
streamRead(20, 1);
} catch (Exception $e) {
echo $e->getMessage();
}
setUTF8Data(false);
endTest($testName);
?>
--EXPECT--
Test "Stream - Read" completed successfully.
Test "Stream - Read" completed successfully.

View file

@ -5,20 +5,23 @@ Verifies the streaming behavior with scrollable resultsets.
--ENV--
PHPT_EXEC=true
--SKIPIF--
<?php require('skipif_versions_old.inc'); ?>
<?
// locale must be set before 1st connection
if ( !isWindows() ) {
setlocale(LC_ALL, "en_US.ISO-8859-1");
}
?>
--FILE--
<?php
require_once('MsCommon.inc');
function streamScroll($noRows, $startRow)
{
$testName = "Stream - Scrollable";
startTest($testName);
setup();
$tableName = "TC55test";
if (! isWindows()) {
$conn1 = AE\connect(array( 'CharacterSet'=>'UTF-8' ));
if (useUTF8Data()) {
$conn1 = AE\connect(array('CharacterSet'=>'UTF-8'));
} else {
$conn1 = AE\connect();
}
@ -84,8 +87,6 @@ function streamScroll($noRows, $startRow)
dropTable($conn1, $tableName);
sqlsrv_close($conn1);
endTest($testName);
}
function verifyStream($stmt, $row, $colIndex)
@ -150,16 +151,37 @@ function checkData($col, $actual, $expected)
return ($success);
}
if (! isWindows()) {
setUTF8Data(true);
// locale must be set before 1st connection
if (!isWindows()) {
setlocale(LC_ALL, "en_US.ISO-8859-1");
}
global $testName;
$testName = "Stream - Scrollable";
// test ansi only if windows or non-UTF8 locales are supported (ODBC 17 and above)
startTest($testName);
if (isWindows() || isLocaleSupported()) {
try {
setUTF8Data(false);
streamScroll(20, 1);
} catch (Exception $e) {
echo $e->getMessage();
}
}
endTest($testName);
// test utf8
startTest($testName);
try {
setUTF8Data(true);
streamScroll(20, 1);
} catch (Exception $e) {
echo $e->getMessage();
}
setUTF8Data(false);
endTest($testName);
?>
--EXPECT--
Test "Stream - Scrollable" completed successfully.
Test "Stream - Scrollable" completed successfully.

View file

@ -1,7 +1,7 @@
--TEST--
Error messages from null result sets
Error messages from nonempty, empty, and null result sets
--DESCRIPTION--
Test that calling sqlsrv_next_result() on a null result set produces the correct error message. Fix for Github 507.
Test that calling sqlsrv_next_result() and fetching on nonempty, empty, and null result sets produces the correct results or error messages.
--SKIPIF--
<?php require('skipif.inc'); ?>
--FILE--
@ -9,13 +9,72 @@ Test that calling sqlsrv_next_result() on a null result set produces the correct
require_once("MsSetup.inc");
require_once("MsCommon.inc");
// These are the error messages we expect at various points below
$errorNoMoreResults = "There are no more results returned by the query.";
$errorNoMoreRows = "There are no more rows in the active result set. Since this result set is not scrollable, no more data may be retrieved.";
$errorNoFields = "The active result for the query contains no fields.";
// Variable function gets an error message that depends on the OS
function getFuncSeqError()
{
if ( strtoupper( substr( php_uname( 's' ),0,3 ) ) === 'WIN' ) {
return "[Microsoft][ODBC Driver Manager] Function sequence error";
} else {
return "[unixODBC][Driver Manager]Function sequence error";
}
}
$errorFuncSeq = 'getFuncSeqError';
// This function takes an array of expected error messages and compares the
// contents to the actual errors
function CheckError($expectedErrors)
{
$actualErrors = sqlsrv_errors();
$sizeActualErrors = 0;
if (!is_null($actualErrors)) {
$sizeActualErrors = sizeof($actualErrors);
}
if (($sizeActualErrors) != sizeof($expectedErrors)) {
echo "Wrong size for error array\n";
print_r($actualErrors);
return;
}
$i = 0;
foreach ($expectedErrors as $e) {
if ($actualErrors[$i]['message'] != $e) {
echo "Wrong error message:\n";
print_r($actualErrors[$i]);
}
$i++;
}
}
function Fetch($stmt, $errors)
{
echo "Fetch...\n";
$result = sqlsrv_fetch_array($stmt);
print_r($result);
CheckError($errors);
}
function NextResult($stmt, $errors)
{
echo "Next result...\n";
sqlsrv_next_result($stmt);
CheckError($errors);
}
$conn = sqlsrv_connect($server, array("Database"=>$databaseName, "uid"=>$uid, "pwd"=>$pwd));
DropTable($conn, 'TestEmptySetTable');
$stmt = sqlsrv_query($conn, "CREATE TABLE TestEmptySetTable ([c1] nvarchar(10),[c2] nvarchar(10))");
$stmt = sqlsrv_query($conn, "INSERT INTO TestEmptySetTable (c1, c2) VALUES ('a', 'b')");
// Create a procedure that can return a result set or can return nothing
// Create a procedure that can return a nonempty result set, an empty result set, or a null result
DropProc($conn, 'TestEmptySetProc');
$stmt = sqlsrv_query($conn, "CREATE PROCEDURE TestEmptySetProc @a nvarchar(10), @b nvarchar(10)
AS SET NOCOUNT ON
@ -24,101 +83,126 @@ $stmt = sqlsrv_query($conn, "CREATE PROCEDURE TestEmptySetProc @a nvarchar(10),
BEGIN
SELECT 'a' as testValue
END
ELSE IF @b='w'
BEGIN
SELECT * FROM TestEmptySetTable WHERE c1 = @b
END
ELSE
BEGIN
UPDATE TestEmptySetTable SET c2 = 'c' WHERE c1 = @a
END
END");
// errors out when reaching the second nextRowset() call
// returned error indicates there are no more results
echo "Return a nonempty result set:\n";
// Call fetch on a nonempty result set
echo "Nonempty result set, call fetch first: ###############################\n";
$stmt = sqlsrv_query($conn,"TestEmptySetProc @a='a', @b='b'");
$result = sqlsrv_fetch_array($stmt);
print_r($result);
sqlsrv_next_result($stmt);
$result = sqlsrv_fetch_array($stmt);
print_r($result);
sqlsrv_next_result($stmt);
Fetch($stmt, []);
NextResult($stmt, []);
Fetch($stmt, [$errorFuncSeq()]);
NextResult($stmt, [$errorNoMoreResults, $errorFuncSeq()]);
print_r(sqlsrv_errors());
// Call next_result on a nonempty result set
echo "Nonempty result set, call next_result first: #########################\n";
// errors out indicating the result set contains no fields
echo "Return an empty result set, call nextRowset on it before fetching anything:\n";
$stmt = sqlsrv_query($conn,"TestEmptySetProc @a='a', @b='b'");
NextResult($stmt, []);
Fetch($stmt, [$errorFuncSeq()]);
NextResult($stmt, [$errorNoMoreResults, $errorFuncSeq()]);
// Call next_result twice in succession on a nonempty result set
echo "Nonempty result set, call next_result twice: #########################\n";
$stmt = sqlsrv_query($conn, "TestEmptySetProc @a='a', @b='b'");
NextResult($stmt, []);
NextResult($stmt, [$errorNoMoreResults]);
// Call fetch on an empty result set
echo "Empty result set, call fetch first: ##################################\n";
$stmt = sqlsrv_query($conn,"TestEmptySetProc @a='a', @b='w'");
Fetch($stmt, []);
NextResult($stmt, []);
Fetch($stmt, [$errorNoMoreRows]);
NextResult($stmt, [$errorNoMoreResults]);
// Call next_result on an empty result set
echo "Empty result set, call next_result first: ############################\n";
$stmt = sqlsrv_query($conn,"TestEmptySetProc @a='a', @b='w'");
NextResult($stmt, []);
Fetch($stmt, [$errorFuncSeq()]);
NextResult($stmt, [$errorNoMoreResults, $errorFuncSeq()]);
// Call next_result twice in succession on an empty result set
echo "Empty result set, call next_result twice: ############################\n";
$stmt = sqlsrv_query($conn, "TestEmptySetProc @a='a', @b='w'");
NextResult($stmt, []);
NextResult($stmt, [$errorNoMoreResults]);
// Call fetch on a null result set
echo "Null result set, call fetch first: ###################################\n";
$stmt = sqlsrv_query($conn, "TestEmptySetProc @a='a', @b='c'");
sqlsrv_next_result($stmt);
print_r(sqlsrv_errors());
Fetch($stmt, [$errorNoFields]);
NextResult($stmt, []);
// errors out indicating the result set contains no fields
echo "Return an empty result set, call fetch on it:\n";
// Call next_result on a null result set
echo "Null result set, call next result first: #############################\n";
$stmt = sqlsrv_query($conn, "TestEmptySetProc @a='a', @b='c'");
$result = sqlsrv_fetch_array($stmt);
print_r($result);
print_r(sqlsrv_errors());
NextResult($stmt, []);
Fetch($stmt, [$errorFuncSeq()]);
// Call next_result twice in succession on a null result set
echo "Null result set, call next result twice: #############################\n";
$stmt = sqlsrv_query($conn, "TestEmptySetProc @a='a', @b='c'");
NextResult($stmt, []);
NextResult($stmt, [$errorNoMoreResults]);
$stmt = sqlsrv_query($conn, "DROP TABLE TestEmptySetTable");
$stmt = sqlsrv_query($conn, "DROP PROCEDURE TestEmptySetProc");
sqlsrv_free_stmt($stmt);
sqlsrv_close($conn);
?>
--EXPECTF--
Return a nonempty result set:
--EXPECT--
Nonempty result set, call fetch first: ###############################
Fetch...
Array
(
[0] => a
[testValue] => a
)
Array
(
[0] => Array
(
[0] => IMSSP
[SQLSTATE] => IMSSP
[1] => -26
[code] => -26
[2] => There are no more results returned by the query.
[message] => There are no more results returned by the query.
)
[1] => Array
(
[0] => HY010
[SQLSTATE] => HY010
[1] => 0
[code] => 0
[2] => [%rMicrosoft|unixODBC%r][%rODBC D|D%rriver Manager]%r[ ]{0,1}%rFunction sequence error
[message] => [%rMicrosoft|unixODBC%r][%rODBC D|D%rriver Manager]%r[ ]{0,1}%rFunction sequence error
)
)
Return an empty result set, call nextRowset on it before fetching anything:
Array
(
[0] => Array
(
[0] => IMSSP
[SQLSTATE] => IMSSP
[1] => -28
[code] => -28
[2] => The active result for the query contains no fields.
[message] => The active result for the query contains no fields.
)
)
Return an empty result set, call fetch on it:
Array
(
[0] => Array
(
[0] => IMSSP
[SQLSTATE] => IMSSP
[1] => -28
[code] => -28
[2] => The active result for the query contains no fields.
[message] => The active result for the query contains no fields.
)
)
Next result...
Fetch...
Next result...
Nonempty result set, call next_result first: #########################
Next result...
Fetch...
Next result...
Nonempty result set, call next_result twice: #########################
Next result...
Next result...
Empty result set, call fetch first: ##################################
Fetch...
Next result...
Fetch...
Next result...
Empty result set, call next_result first: ############################
Next result...
Fetch...
Next result...
Empty result set, call next_result twice: ############################
Next result...
Next result...
Null result set, call fetch first: ###################################
Fetch...
Next result...
Null result set, call next result first: #############################
Next result...
Fetch...
Null result set, call next result twice: #############################
Next result...
Next result...

View file

@ -1,20 +1,32 @@
--TEST--
streaming large amounts of data into a database and getting it out as a string exactly the same.
--SKIPIF--
<?php require('skipif.inc'); ?>
<?
// locale must be set before 1st connection
if ( !isWindows() ) {
setlocale(LC_ALL, "en_US.ISO-8859-1");
}
php require('skipif.inc');
?>
--FILE--
<?php
require_once("MsCommon.inc");
function runtest()
{
set_time_limit(0);
sqlsrv_configure('WarningsReturnAsErrors', 0);
sqlsrv_configure('LogSeverity', SQLSRV_LOG_SEVERITY_ALL);
sqlsrv_configure('LogSubsystems', SQLSRV_LOG_SYSTEM_OFF);
require_once("MsCommon.inc");
$notWindows = (! isWindows());
// required charset UTF-8 when NOT running in Windows
$conn1 = ($notWindows) ? connect(array( 'CharacterSet'=>'UTF-8' )) : connect() ;
$useUTF8 = useUTF8Data();
if ($useUTF8) {
$conn1 = connect(array('CharacterSet'=>'UTF-8'));
} else {
$conn1 = connect();
}
if ($conn1 === false) {
fatalError("Failed to connect");
}
@ -31,18 +43,17 @@ streaming large amounts of data into a database and getting it out as a string e
sqlsrv_free_stmt($stmt2);
sqlsrv_close($conn1);
if ($notWindows) {
$conn2 = connect(array( 'CharacterSet' =>'utf-8' ));
if ($useUTF8) {
$conn2 = connect(array('CharacterSet'=>'UTF-8'));
} else {
$conn2 = connect();
}
if ($conn2 === false) {
echo "sqlsrv_connect failed 2nd.\n";
die(print_r(sqlsrv_errors(), true));
}
if ($notWindows) {
if ($useUTF8) {
require('test_stream_large_data_UTF8.inc');
GenerateInputUTF8Data();
} else {
@ -147,27 +158,27 @@ streaming large amounts of data into a database and getting it out as a string e
$metadata1 = sqlsrv_field_metadata($stmt8);
$count = count($metadata1);
sqlsrv_fetch($stmt8);
$value1 = GetField($stmt8, 13, $notWindows);
$value1 = GetField($stmt8, 13, $useUTF8);
$lens1[$i++] = strlen($value1) . "\n";
$fout = fopen("varchar_max.out", "w");
fwrite($fout, $value1);
fclose($fout);
$value2 = GetField($stmt8, 16, $notWindows);
$value2 = GetField($stmt8, 16, $useUTF8);
$lens1[$i++] = strlen($value2) . "\n";
$fout = fopen("nvarchar_max.out", "w");
fwrite($fout, $value2);
fclose($fout);
$value3 = GetField($stmt8, 17, $notWindows);
$value3 = GetField($stmt8, 17, $useUTF8);
$lens1[$i++] = strlen($value3) . "\n";
$fout = fopen("text.out", "w");
fwrite($fout, $value3);
fclose($fout);
$value4 = GetField($stmt8, 18, $notWindows);
$value4 = GetField($stmt8, 18, $useUTF8);
$lens1[$i++] = strlen($value4) . "\n";
$fout = fopen("ntext.out", "w");
fwrite($fout, $value4);
fclose($fout);
$value5 = GetField($stmt8, 27, $notWindows);
$value5 = GetField($stmt8, 27, $useUTF8);
$lens1[$i++] = strlen($value5) . "\n";
$fout = fopen("xml.out", "w");
fwrite($fout, $value5);
@ -189,57 +200,78 @@ streaming large amounts of data into a database and getting it out as a string e
$metadata1 = sqlsrv_field_metadata($stmt8);
$count = count($metadata1);
sqlsrv_fetch($stmt8);
$value1 = GetField($stmt8, 13, $notWindows);
$value1 = GetField($stmt8, 13, $useUTF8);
$lens2[$i++] = strlen($value1);
$value2 = GetField($stmt8, 16, $notWindows);
$value2 = GetField($stmt8, 16, $useUTF8);
$lens2[$i++] = strlen($value2) . "\n";
$value3 = GetField($stmt8, 17, $notWindows);
$value3 = GetField($stmt8, 17, $useUTF8);
$lens2[$i++] = strlen($value3) . "\n";
$value4 = GetField($stmt8, 18, $notWindows);
$value4 = GetField($stmt8, 18, $useUTF8);
$lens2[$i++] = strlen($value4) . "\n";
$value5 = GetField($stmt8, 27, $notWindows);
$value5 = GetField($stmt8, 27, $useUTF8);
$lens2[$i++] = strlen($value5) . "\n";
CompareLengths($filesizes, $lens1, $lens2, $i, $notWindows);
CompareLengths($filesizes, $lens1, $lens2, $i, $useUTF8);
echo "Test finished\n";
sqlsrv_free_stmt($stmt8);
sqlsrv_close($conn2);
}
function GetField($stmt, $idx, $notWindows)
{
if ($notWindows) {
return sqlsrv_get_field($stmt, $idx, SQLSRV_PHPTYPE_STRING('UTF-8'));
} else {
return sqlsrv_get_field($stmt, $idx, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR));
}
function GetField($stmt, $idx, $useUTF8)
{
if ($useUTF8) {
return sqlsrv_get_field($stmt, $idx, SQLSRV_PHPTYPE_STRING('UTF-8'));
} else {
return sqlsrv_get_field($stmt, $idx, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR));
}
}
function CompareLengths($filesizes, $lens1, $lens2, $count, $notWindows)
{
if ($notWindows) {
// in Linux or Mac, same field should return same length, and strlen() for Unicode data is different
for ($i = 0; $i < $count; $i++) {
$length = $filesizes[$i];
if ($lens1[$i] != $length || $lens2[$i] != $length) {
echo "Data length mismatched!\n";
}
}
} else {
// in Windows, all lengths are equal
$length = 1048576; // number of characters in the data (in ANSI encoding)
for ($i = 0; $i < $count; $i++) {
if ($filesizes[$i] != $length) {
echo "File $i size unexpected\n";
}
if ($lens1[$i] != $length || $lens2[$i] != $length) {
echo "Data length mismatched!\n";
}
function CompareLengths($filesizes, $lens1, $lens2, $count, $useUTF8)
{
if ($useUTF8) {
// in Linux or Mac, same field should return same length, and strlen() for Unicode data is different
for ($i = 0; $i < $count; $i++) {
$length = $filesizes[$i];
if ($lens1[$i] != $length || $lens2[$i] != $length) {
echo "Data length mismatched!\n";
}
}
} else {
// in Windows, all lengths are equal
$length = 1048576; // number of characters in the data (in ANSI encoding)
for ($i = 0; $i < $count; $i++) {
if ($filesizes[$i] != $length) {
echo "File $i size unexpected\n";
}
if ($lens1[$i] != $length || $lens2[$i] != $length) {
echo "Data length mismatched!\n";
}
}
}
}
// locale must be set before 1st connection
if (!isWindows()) {
setlocale(LC_ALL, "en_US.ISO-8859-1");
}
// test ansi only if windows or non-UTF8 locales are supported (ODBC 17 and above)
if (isWindows() || isLocaleSupported()) {
setUTF8Data(false);
runtest();
}
else {
echo "Test finished\n";
}
// test utf8
setUTF8Data(true);
runtest();
?>
--EXPECT--
Test finished
Test finished