Skip to content

Commit

Permalink
Merge pull request #595 from david-puglielli/nextrowset-revert
Browse files Browse the repository at this point in the history
Revert nextRowset changes
  • Loading branch information
david-puglielli authored Nov 17, 2017
2 parents 04f5034 + 0088d61 commit 01dd22c
Show file tree
Hide file tree
Showing 4 changed files with 383 additions and 277 deletions.
27 changes: 0 additions & 27 deletions source/pdo_sqlsrv/pdo_stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 0 additions & 26 deletions source/sqlsrv/stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 );

Expand Down
279 changes: 179 additions & 100 deletions test/functional/pdo_sqlsrv/pdo_empty_result_error.phpt
Original file line number Diff line number Diff line change
@@ -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...
Loading

0 comments on commit 01dd22c

Please sign in to comment.