Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added empty result set check to nextRowset() #520

Merged
merged 30 commits into from
Oct 3, 2017
Merged

Added empty result set check to nextRowset() #520

merged 30 commits into from
Oct 3, 2017

Conversation

david-puglielli
Copy link
Contributor

@david-puglielli david-puglielli commented Sep 7, 2017

This is a fix for the bug discussed in issue #507.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.003%) to 74.249% when pulling 83631cd on david-puglielli:nextrowset-fix into 9e695d2 on Microsoft:dev.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.08%) to 74.332% when pulling f4117a8 on david-puglielli:nextrowset-fix into 9e695d2 on Microsoft:dev.

CHECK_CUSTOM_ERROR( has_fields == 0, driver_stmt, SQLSRV_ERROR_NO_FIELDS ) {
throw core::CoreException();
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From this page it seems valid and accurate to call this without making another trip to the server. Please confirm.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since a statement has to be executed for the user to call nextRowset, there is no trip to the server made here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there an equivalent scenario in the sqlsrv side?
Based on your comment it seems that fetch() also invokes SQLNumResultCols(), right? Would that be a duplicate?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I've verified the same problem exists on the sqlsrv side. But I think core_sqlsrv_fetch is never called if nextRowset is called before fetch, which is why we're catching the error here. I'll add a fix and test for sqlsrv.

@david-puglielli
Copy link
Contributor Author

david-puglielli commented Sep 22, 2017

Moved the error check to core_sqlsrv_next_result in core_stmt.cpp.

Edit: Moved it back because it was causing test failures.

@coveralls
Copy link

Coverage Status

Coverage decreased (-1.3%) to 72.967% when pulling 8666ea6 on david-puglielli:nextrowset-fix into 9e695d2 on Microsoft:dev.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.8%) to 73.406% when pulling 2c28d0b on david-puglielli:nextrowset-fix into 9e695d2 on Microsoft:dev.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.8%) to 73.406% when pulling f11c208 on david-puglielli:nextrowset-fix into 9e695d2 on Microsoft:dev.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.09%) to 73.366% when pulling 66a12d3 on david-puglielli:nextrowset-fix into ca9ba63 on Microsoft:dev.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.05%) to 73.321% when pulling d56446c on david-puglielli:nextrowset-fix into ca9ba63 on Microsoft:dev.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.003%) to 73.276% when pulling 257b1cd on david-puglielli:nextrowset-fix into ca9ba63 on Microsoft:dev.

@coveralls
Copy link

coveralls commented Sep 28, 2017

Coverage Status

Coverage increased (+0.003%) to 73.276% when pulling 7315d44 on david-puglielli:nextrowset-fix into ca9ba63 on Microsoft:dev.

CHECK_CUSTOM_ERROR( !has_result, driver_stmt, SQLSRV_ERROR_NO_FIELDS ) {
throw core::CoreException();
}
}
Copy link
Contributor

@yukiwongky yukiwongky Sep 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't quite understand. I thought PSOStatement::nextRowset should only be called on a fetched statement. The condition for if says fetched is NOT called, and throws an exception if there is no result. How can there any result if the statement was never fetched?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nextRowset() should only be called on a fetched statement, but there is nothing stopping the user from calling nextRowset immediately after executing, and we need to handle that case. In issue 507 it occurs when going into the 'else' part of stored procedure that returns a result set in the 'if' but not the 'else' branch, so it can happen that user does call nextRowset first accidentally via a stored procedure.

This code catches the case where nextRowset() is called before fetch on a null result set, giving the appropriate error message; before, there would be no error given. In the case where nextRowset is called before fetch on non-null result set, core_sqlsrv_has_any_result will return true and the error is not triggered. In the event that nextRowset is called after fetch() and the result set is null, that will be handled in core_sqlsrv_next_result() (or by the check for past_next_result_end above). I think that covers all the relevant cases...

Copy link
Contributor

@yukiwongky yukiwongky Sep 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea I was confused what happens when fetch isn't called and has_result is true, but now it's more clear. Can you put a comment saying if fetch_called is false and has_result is true, core_sqlsrv_next_result will handle it.

$conn = new PDO( "sqlsrv:Server = $server; Database = $databaseName; ", $uid, $pwd );
$conn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );

$stmt = $conn->query("IF OBJECT_ID('TestEmptySetTable', 'U') IS NOT NULL DROP TABLE TestEmptySetTable");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There a DropTable and DropProc function you can use in MsCommon

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.04%) to 73.23% when pulling 494527d on david-puglielli:nextrowset-fix into ca9ba63 on Microsoft:dev.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.003%) to 73.276% when pulling dc6e2a3 on david-puglielli:nextrowset-fix into ca9ba63 on Microsoft:dev.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.003%) to 73.276% when pulling dc6e2a3 on david-puglielli:nextrowset-fix into ca9ba63 on Microsoft:dev.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.003%) to 73.276% when pulling 4674f58 on david-puglielli:nextrowset-fix into ca9ba63 on Microsoft:dev.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.003%) to 73.276% when pulling 4674f58 on david-puglielli:nextrowset-fix into ca9ba63 on Microsoft:dev.

Copy link
Contributor

@yitam yitam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps add a couple of tests on prepare (not query / exec) on a regular SELECT query (not a stored procedure) then fetch next rowset, with and without client buffers?

--TEST--
Test that calling nextRowset() on an empty result set produces the correct error message. Fix for Github 507.
--SKIPIF--
<?php require('skipif.inc'); ?>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please try to limit the length of test title. If you have more please put it in --DESCRIPTION-- instead.
For tests, it's a good practice to drop test tables / procedures at the end to leave no trace.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do drop them at the end, but I kept the drop functions at the beginning too just in case...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahhh... I see! In the beginning you use DropTable and DropProc but later you simply use queries.

$stmt = sqlsrv_query($conn, "DROP TABLE TestEmptySetTable");
$stmt = sqlsrv_query($conn, "DROP PROCEDURE TestEmptySetProc");
sqlsrv_close($conn);
?>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just for practice, probably free the statement object too before closing the connection?

sqlsrv_close($conn);
?>
--EXPECTF--
Return a nonempty result set:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why EXPECTF?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

%s in the output may be either 'Microsoft' on Windows or 'unixODBC' on unix systems, and EXPECTREGEX seemed like overkill for one string in a long output like this.

@david-puglielli
Copy link
Contributor Author

I'm not sure how useful testing with a regular SELECT statement would be, because I don't think the result set could ever be null in that case. At most you can have an empty result set with no rows but at least one column, or you select from a nonexistent column, which yields a different error message. These are not the same as a null result set, in which there are no rows and no columns, and it's why I needed to use the stored procedure in the tests to trigger a null result.

Copy link
Contributor

@yitam yitam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve assuming you have checked there's no regression

--TEST--
Test that calling nextRowset() on an empty result set produces the correct error message. Fix for Github 507.
--SKIPIF--
<?php require('skipif.inc'); ?>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahhh... I see! In the beginning you use DropTable and DropProc but later you simply use queries.

Copy link
Contributor

@yitam yitam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My oversight. Looks like you still have test failures. Please fix those.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.003%) to 73.276% when pulling 4349a3c on david-puglielli:nextrowset-fix into ca9ba63 on Microsoft:dev.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.003%) to 73.276% when pulling 70b20a2 on david-puglielli:nextrowset-fix into ca9ba63 on Microsoft:dev.

Copy link
Contributor

@yitam yitam left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the test that you had to fix the regex in order to pass, consider only checking for the first error (the PHP related error)

@david-puglielli david-puglielli merged commit a1d6130 into microsoft:dev Oct 3, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants