Revert next result changes, improve tests

This commit is contained in:
David Puglielli 2017-11-15 17:39:00 -08:00
parent 07ac237def
commit 633024c22b
4 changed files with 703 additions and 277 deletions

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

@ -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--
@ -16,7 +16,7 @@ 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
@ -25,23 +25,35 @@ $stmt = $conn->query("CREATE PROCEDURE TestEmptySetProc @a nvarchar(10), @b nvar
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";
try
{
$stmt = $conn->query("TestEmptySetProc @a='a', @b='b'");
$result = $stmt->fetchAll();
echo "First fetch...\n";
$result = $stmt->fetchObject();
print_r($result);
echo "Next result...\n";
$stmt->nextRowset();
$result = $stmt->fetchAll();
echo "Fetch...\n";
$result = $stmt->fetch();
print_r($result);
echo "Next result...\n";
$stmt->nextRowset();
}
catch(Exception $e)
@ -49,11 +61,21 @@ 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";
// Call next_result on a nonempty result set
echo "Nonempty result set, call next_result first: #########################\n";
try
{
$stmt = $conn->query("TestEmptySetProc @a='a', @b='c'");
$stmt = $conn->query("TestEmptySetProc @a='a', @b='b'");
echo "Next result...\n";
$stmt->nextRowset();
echo "Fetch...\n";
$result = $stmt->fetchObject();
print_r($result);
echo "Next result...\n";
$stmt->nextRowset();
}
catch(Exception $e)
@ -61,13 +83,141 @@ 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";
// Call next_result twice in succession on a nonempty result set
echo "Nonempty result set, call next_result twice: #########################\n";
try
{
$stmt = $conn->query("TestEmptySetProc @a='a', @b='b'");
echo "Next result...\n";
$stmt->nextRowset();
echo "Next result...\n";
$stmt->nextRowset();
}
catch(Exception $e)
{
echo $e->getMessage()."\n";
}
// Call fetch on an empty result set
echo "Empty result set, call fetch first: ##################################\n";
try
{
$stmt = $conn->query("TestEmptySetProc @a='a', @b='w'");
echo "First fetch...\n";
$result = $stmt->fetchObject();
print_r($result);
echo "Next result...\n";
$stmt->nextRowset();
echo "Fetch...\n";
$result = $stmt->fetchObject();
print_r($result);
echo "Next result...\n";
$stmt->nextRowset();
}
catch(Exception $e)
{
echo $e->getMessage()."\n";
}
// Call next_result on an empty result set
echo "Empty result set, call next_result first: ############################\n";
try
{
$stmt = $conn->query("TestEmptySetProc @a='a', @b='w'");
echo "First go to next result...\n";
$stmt->nextRowset();
echo "Fetch...\n";
$result = $stmt->fetchObject();
print_r($result);
echo "Next result...\n";
$stmt->nextRowset();
}
catch(Exception $e)
{
echo $e->getMessage()."\n";
}
// Call next_result twice in succession on an empty result set
echo "Empty result set, call next_result twice: ############################\n";
try
{
$stmt = $conn->query("TestEmptySetProc @a='a', @b='w'");
echo "Next result...\n";
$stmt->nextRowset();
echo "Next result...\n";
$stmt->nextRowset();
}
catch(Exception $e)
{
echo $e->getMessage()."\n";
}
// Call fetch on a null result set
echo "Null result set, call fetch first: ###################################\n";
try
{
$stmt = $conn->query("TestEmptySetProc @a='a', @b='c'");
$result = $stmt->fetchAll();
echo "Fetch...\n";
$result = $stmt->fetchObject();
print_r($result);
echo "Next result...\n";
$stmt->nextRowset();
}
catch(Exception $e)
{
echo $e->getMessage()."\n";
}
// Call next_result on a null result set
echo "Null result set, call next result first: #############################\n";
try
{
$stmt = $conn->query("TestEmptySetProc @a='a', @b='c'");
echo "Next result...\n";
$stmt->nextRowset();
echo "Fetch...\n";
$result = $stmt->fetchObject();
print_r($result);
}
catch(Exception $e)
{
echo $e->getMessage()."\n";
}
// Call next_result twice in succession on a null result set
echo "Null result set, call next result twice: #############################\n";
try
{
$stmt = $conn->query("TestEmptySetProc @a='a', @b='c'");
echo "Next result...\n";
$stmt->nextRowset();
echo "Next result...\n";
$stmt->nextRowset();
}
catch(Exception $e)
{
@ -76,25 +226,51 @@ catch(Exception $e)
$stmt = $conn->query("DROP TABLE TestEmptySetTable");
$stmt = $conn->query("DROP PROCEDURE TestEmptySetProc");
$stmt = null;
$conn = null;
?>
--EXPECT--
Return a nonempty result set:
Array
(
[0] => Array
Nonempty result set, call fetch first: ###############################
First fetch...
stdClass Object
(
[testValue] => a
[0] => a
)
)
Array
(
)
Next result...
Fetch...
Next result...
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:
Nonempty result set, call next_result first: #########################
Next result...
Fetch...
Next result...
SQLSTATE[IMSSP]: There are no more results returned by the query.
Nonempty result set, call next_result twice: #########################
Next result...
Next result...
SQLSTATE[IMSSP]: There are no more results returned by the query.
Empty result set, call fetch first: ##################################
First fetch...
Next result...
Fetch...
Next result...
SQLSTATE[IMSSP]: There are no more results returned by the query.
Empty result set, call next_result first: ############################
First go to next result...
Fetch...
Next result...
SQLSTATE[IMSSP]: There are no more results returned by the query.
Empty result set, call next_result twice: ############################
Next result...
Next result...
SQLSTATE[IMSSP]: There are no more results returned by the query.
Null result set, call fetch first: ###################################
Fetch...
SQLSTATE[IMSSP]: The active result for the query contains no fields.
Null result set, call next result first: #############################
Next result...
Fetch...
Null result set, call next result twice: #############################
Next result...
Next result...
SQLSTATE[IMSSP]: There are no more results returned by the query.

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--
@ -15,7 +15,7 @@ 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,53 +24,193 @@ $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);
echo "First fetch...\n";
$result = sqlsrv_fetch_array($stmt);//$result=sqlsrv_get_field($stmt,0);
print_r($result);
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'");
echo "Next result...\n";
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'");
echo "Fetch...\n";
$result = sqlsrv_fetch_array($stmt);
print_r($result);
print_r(sqlsrv_errors());
echo "Next result...\n";
sqlsrv_next_result($stmt);
print_r(sqlsrv_errors());
// 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'");
echo "Next result...\n";
sqlsrv_next_result($stmt);
print_r(sqlsrv_errors());
echo "Fetch...\n";
$result = sqlsrv_fetch_array($stmt);
print_r($result);
print_r(sqlsrv_errors());
echo "Next result...\n";
sqlsrv_next_result($stmt);
print_r(sqlsrv_errors());
// 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'");
echo "Next result...\n";
sqlsrv_next_result($stmt);
print_r(sqlsrv_errors());
echo "Next result...\n";
sqlsrv_next_result($stmt);
print_r(sqlsrv_errors());
// Call fetch on an empty result set
echo "Empty result set, call fetch first: ##################################\n";
$stmt = sqlsrv_query($conn,"TestEmptySetProc @a='a', @b='w'");
echo "First fetch...\n";
$result = sqlsrv_fetch_array($stmt);
print_r($result);
print_r(sqlsrv_errors());
echo "Next result...\n";
sqlsrv_next_result($stmt);
print_r(sqlsrv_errors());
echo "Fetch...\n";
$result = sqlsrv_fetch_array($stmt);
print_r($result);
print_r(sqlsrv_errors());
echo "Next result...\n";
sqlsrv_next_result($stmt);
print_r(sqlsrv_errors());
// 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'");
echo "First go to next result...\n";
sqlsrv_next_result($stmt);
print_r(sqlsrv_errors());
echo "Fetch...\n";
$result = sqlsrv_fetch_array($stmt);
print_r($result);
print_r(sqlsrv_errors());
echo "Next result...\n";
sqlsrv_next_result($stmt);
print_r(sqlsrv_errors());
// 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'");
echo "Next result...\n";
sqlsrv_next_result($stmt);
print_r(sqlsrv_errors());
echo "Next result...\n";
sqlsrv_next_result($stmt);
print_r(sqlsrv_errors());
// Call fetch on a null result set
echo "Null result set, call fetch first: ###################################\n";
$stmt = sqlsrv_query($conn, "TestEmptySetProc @a='a', @b='c'");
echo "Fetch...\n";
$result = sqlsrv_fetch_array($stmt);
print_r($result);
print_r(sqlsrv_errors());
echo "Next result...\n";
sqlsrv_next_result($stmt);
print_r(sqlsrv_errors());
// 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'");
echo "Next result...\n";
sqlsrv_next_result($stmt);
print_r(sqlsrv_errors());
echo "Fetch...\n";
$result = sqlsrv_fetch_array($stmt);
print_r(sqlsrv_errors());
// 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'");
echo "Next result...\n";
sqlsrv_next_result($stmt);
print_r(sqlsrv_errors());
echo "Next result...\n";
sqlsrv_next_result($stmt);
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:
Nonempty result set, call fetch first: ###############################
First fetch...
Array
(
[0] => a
[testValue] => a
)
Next result...
Fetch...
Array
(
[0] => Array
(
[0] => HY010
[SQLSTATE] => HY010
[1] => 0
[code] => 0
[2] => [%rMicrosoft|unixODBC%r][%rODBC D|D%rriver Manager]Function sequence error
[message] => [%rMicrosoft|unixODBC%r][%rODBC D|D%rriver Manager]Function sequence error
)
)
Next result...
Array
(
[0] => Array
@ -89,12 +229,156 @@ Array
[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
[2] => [%rMicrosoft|unixODBC%r][%rODBC D|D%rriver Manager]Function sequence error
[message] => [%rMicrosoft|unixODBC%r][%rODBC D|D%rriver Manager]Function sequence error
)
)
Return an empty result set, call nextRowset on it before fetching anything:
Nonempty result set, call next_result first: #########################
Next result...
Fetch...
Array
(
[0] => Array
(
[0] => HY010
[SQLSTATE] => HY010
[1] => 0
[code] => 0
[2] => [%rMicrosoft|unixODBC%r][%rODBC D|D%rriver Manager]Function sequence error
[message] => [%rMicrosoft|unixODBC%r][%rODBC D|D%rriver Manager]Function sequence error
)
)
Next result...
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]Function sequence error
[message] => [%rMicrosoft|unixODBC%r][%rODBC D|D%rriver Manager]Function sequence error
)
)
Nonempty result set, call next_result twice: #########################
Next result...
Next result...
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.
)
)
Empty result set, call fetch first: ##################################
First fetch...
Next result...
Fetch...
Array
(
[0] => Array
(
[0] => IMSSP
[SQLSTATE] => IMSSP
[1] => -22
[code] => -22
[2] => There are no more rows in the active result set. Since this result set is not scrollable, no more data may be retrieved.
[message] => There are no more rows in the active result set. Since this result set is not scrollable, no more data may be retrieved.
)
)
Next result...
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.
)
)
Empty result set, call next_result first: ############################
First go to next result...
Fetch...
Array
(
[0] => Array
(
[0] => HY010
[SQLSTATE] => HY010
[1] => 0
[code] => 0
[2] => [%rMicrosoft|unixODBC%r][%rODBC D|D%rriver Manager]Function sequence error
[message] => [%rMicrosoft|unixODBC%r][%rODBC D|D%rriver Manager]Function sequence error
)
)
Next result...
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]Function sequence error
[message] => [%rMicrosoft|unixODBC%r][%rODBC D|D%rriver Manager]Function sequence error
)
)
Empty result set, call next_result twice: ############################
Next result...
Next result...
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.
)
)
Null result set, call fetch first: ###################################
Fetch...
Array
(
[0] => Array
@ -108,17 +392,36 @@ Array
)
)
Return an empty result set, call fetch on it:
Next result...
Null result set, call next result first: #############################
Next result...
Fetch...
Array
(
[0] => Array
(
[0] => HY010
[SQLSTATE] => HY010
[1] => 0
[code] => 0
[2] => [%rMicrosoft|unixODBC%r][%rODBC D|D%rriver Manager]Function sequence error
[message] => [%rMicrosoft|unixODBC%r][%rODBC D|D%rriver Manager]Function sequence error
)
)
Null result set, call next result twice: #############################
Next result...
Next result...
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.
[1] => -26
[code] => -26
[2] => There are no more results returned by the query.
[message] => There are no more results returned by the query.
)
)