Merge branch 'dev' into codecov
This commit is contained in:
commit
33b4922921
28
CHANGELOG.md
28
CHANGELOG.md
|
@ -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:
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
@ -79,4 +83,4 @@ RUN chmod +x ./entrypoint.sh
|
|||
CMD /bin/bash ./entrypoint.sh
|
||||
|
||||
ENV REPORT_EXIT_STATUS 1
|
||||
ENV TEST_PHP_EXECUTABLE /usr/bin/php
|
||||
ENV TEST_PHP_EXECUTABLE /usr/bin/php
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -560,32 +560,6 @@ PHP_FUNCTION( sqlsrv_next_result )
|
|||
PROCESS_PARAMS( stmt, "r", _FN_, 0 );
|
||||
|
||||
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 );
|
||||
|
||||
|
|
|
@ -1,100 +1,179 @@
|
|||
--TEST--
|
||||
Error messages from null result sets
|
||||
--DESCRIPTION--
|
||||
Test that calling nextRowset() on an empty result set produces the correct error message. Fix for Github 507.
|
||||
--SKIPIF--
|
||||
<?php require('skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsSetup.inc");
|
||||
require_once("MsCommon.inc");
|
||||
|
||||
$conn = new PDO( "sqlsrv:Server = $server; Database = $databaseName; ", $uid, $pwd );
|
||||
$conn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
|
||||
|
||||
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
|
||||
DropProc($conn, 'TestEmptySetProc');
|
||||
$stmt = $conn->query("CREATE PROCEDURE TestEmptySetProc @a nvarchar(10), @b nvarchar(10)
|
||||
AS SET NOCOUNT ON
|
||||
BEGIN
|
||||
IF @b='b'
|
||||
BEGIN
|
||||
SELECT 'a' as testValue
|
||||
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";
|
||||
}
|
||||
|
||||
// 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";
|
||||
}
|
||||
|
||||
// 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";
|
||||
}
|
||||
|
||||
$stmt = $conn->query("DROP TABLE TestEmptySetTable");
|
||||
$stmt = $conn->query("DROP PROCEDURE TestEmptySetProc");
|
||||
|
||||
$conn = null;
|
||||
?>
|
||||
--EXPECT--
|
||||
Return a nonempty result set:
|
||||
Array
|
||||
(
|
||||
[0] => Array
|
||||
(
|
||||
[testValue] => a
|
||||
[0] => 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.
|
||||
--TEST--
|
||||
Error messages from nonempty, empty, and null result sets
|
||||
--DESCRIPTION--
|
||||
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--
|
||||
<?php
|
||||
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_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 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'
|
||||
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");
|
||||
|
||||
// Call fetch on a nonempty result set
|
||||
echo "Nonempty result set, call fetch first: ###############################\n";
|
||||
|
||||
$stmt = $conn->query("TestEmptySetProc @a='a', @b='b'");
|
||||
Fetch($stmt);
|
||||
NextResult($stmt);
|
||||
Fetch($stmt);
|
||||
NextResult($stmt, $errorNoMoreResults);
|
||||
|
||||
// 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--
|
||||
Nonempty result set, call fetch first: ###############################
|
||||
Fetch...
|
||||
stdClass Object
|
||||
(
|
||||
[testValue] => a
|
||||
)
|
||||
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...
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
*/
|
||||
|
||||
|
||||
function getTestData($index)
|
||||
function getTestData_UTF8($index)
|
||||
{
|
||||
$inputs = null;
|
||||
switch ($index)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1184,4 +1185,4 @@ function getSampleData($k)
|
|||
}
|
||||
}
|
||||
|
||||
?>
|
||||
?>
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -1,124 +1,208 @@
|
|||
--TEST--
|
||||
Error messages from null result sets
|
||||
--DESCRIPTION--
|
||||
Test that calling sqlsrv_next_result() on a null result set produces the correct error message. Fix for Github 507.
|
||||
--SKIPIF--
|
||||
<?php require('skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsSetup.inc");
|
||||
require_once("MsCommon.inc");
|
||||
|
||||
$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
|
||||
DropProc($conn, 'TestEmptySetProc');
|
||||
$stmt = sqlsrv_query($conn, "CREATE PROCEDURE TestEmptySetProc @a nvarchar(10), @b nvarchar(10)
|
||||
AS SET NOCOUNT ON
|
||||
BEGIN
|
||||
IF @b='b'
|
||||
BEGIN
|
||||
SELECT 'a' as testValue
|
||||
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";
|
||||
|
||||
$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);
|
||||
|
||||
print_r(sqlsrv_errors());
|
||||
|
||||
// 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='c'");
|
||||
sqlsrv_next_result($stmt);
|
||||
print_r(sqlsrv_errors());
|
||||
|
||||
// errors out indicating the result set contains no fields
|
||||
echo "Return an empty result set, call fetch on it:\n";
|
||||
|
||||
$stmt = sqlsrv_query($conn, "TestEmptySetProc @a='a', @b='c'");
|
||||
$result = sqlsrv_fetch_array($stmt);
|
||||
print_r($result);
|
||||
print_r(sqlsrv_errors());
|
||||
|
||||
$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:
|
||||
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.
|
||||
)
|
||||
|
||||
)
|
||||
--TEST--
|
||||
Error messages from nonempty, empty, and null result sets
|
||||
--DESCRIPTION--
|
||||
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--
|
||||
<?php
|
||||
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 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
|
||||
BEGIN
|
||||
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");
|
||||
|
||||
// Call fetch on a nonempty result set
|
||||
echo "Nonempty result set, call fetch first: ###############################\n";
|
||||
|
||||
$stmt = sqlsrv_query($conn,"TestEmptySetProc @a='a', @b='b'");
|
||||
Fetch($stmt, []);
|
||||
NextResult($stmt, []);
|
||||
Fetch($stmt, [$errorFuncSeq()]);
|
||||
NextResult($stmt, [$errorNoMoreResults, $errorFuncSeq()]);
|
||||
|
||||
// Call next_result on a nonempty result set
|
||||
echo "Nonempty result set, call next_result first: #########################\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'");
|
||||
Fetch($stmt, [$errorNoFields]);
|
||||
NextResult($stmt, []);
|
||||
|
||||
// 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'");
|
||||
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);
|
||||
?>
|
||||
--EXPECT--
|
||||
Nonempty result set, call fetch first: ###############################
|
||||
Fetch...
|
||||
Array
|
||||
(
|
||||
[0] => a
|
||||
[testValue] => a
|
||||
)
|
||||
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...
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue