PDOStatement::nextRowSet() is broken in MySQL 5.6.16 on Windows - php

This question without an accepted answer raises a catastrophic problem with MySQL that I am experiencing on MySQL version 5.6.16 on Windows in a modified form.
The problem is easily reproducible: I include it here (copied from the above-linked question, but with changes applicable to my code):
$pdo = /* connection stuff here */
$sql = "call test();"; // call stored procedure - see below
$statement = $connection->query($sql);
do {
$rowset = $statement->fetchAll(PDO::FETCH_ASSOC);
if($rowset) {
// Do stuff with $rowset
}
} while($statement->nextRowset());
Here is the definition of the stored procedure test:
DELIMITER $$
DROP PROCEDURE IF EXISTS `test`$$
CREATE PROCEDURE `test`()
BEGIN
SELECT 1; SELECT 2; SELECT 3; SELECT 4;
END$$
DELIMITER ;
The only difference between my code, and the above-linked code, is that I pack the SQL query into a stored procedure.
In my case, the while statement returns true four times, rather than three times (it should be just three times). After the fourth time, fetchAll(...) throws a SQLSTATE[HY000]: General error error.
Unfortunately, this problem is catastrophic. There is no other way with PDO to iterate to following rowsets other than using the nextRowSet() function. Therefore, I may revert to a previous version of MySQL in order to work around this issue.
I have found two links that seem to indicate this issue, listed here:
https://bugs.php.net/bug.php?id=67130 and
http://permalink.gmane.org/gmane.comp.php.devel/81518
I would appreciate a confirmation that this is, indeed, a bug with version 5.6.16 of MySQL on Windows. Even more, I would appreciate a workaround. Thanks.

I had same problem with PDO::nextRowset(), as it returns true even there are no more rowsets available, therefore when calling fetchAll(), it raises exception HY000. (tested on PHP 5.5.12 windows, Mysql 5.5.17 linux)
A workaround for this problem is to check number of columns with method PDO::columnCount() before fetching rowset. If it is non-zero, you have a valid rowset, and thus you could call PDO::fetchAll().
Even if PDO::nextRowset() reports true, columnCount() will report number of columns before moving to next rowset.
Example:
while ($objQuery->columnCount()) {
$tab[] = $objQuery->fetchAll(\PDO::FETCH_ASSOC);
$objQuery->nextRowset();
}

If the problem is being caused by multiple result sets, then maybe the best work around would be to find ways of avoiding multiple result sets in a single query.
For example, if the expected results are in the same format (which I expect they probably are because you intend to use the same code to deal with all of them) then you could potentially use UNION to compound the four queries together.
SELECT 1
UNION ALL
SELECT 2
UNION ALL
SELECT 3
UNION ALL
SELECT 4;
Failing that, you can execute a separate query for each SELECT and hand the result off to a function to do the processing.

Related

PHP 5.5's (MS) SQL server functions not working as expected

My sys admin is upgrading my PHP server from 5.2 to 5.5 . As a result, the mssql family of functions is gone and I have to update my code (it seems to either the odbc functions or sqlsrv functions). Unfortunately, neither seems to be working correctly for anything beyond simple queries.
I've reduced one of the problematic queries down to the following two variants (middle line added is the only change):
IF OBJECT_ID('tempdb..#i') IS NOT NULL BEGIN DROP TABLE #i END
SELECT 'value' as test
IF OBJECT_ID('tempdb..#i') IS NOT NULL BEGIN DROP TABLE #i END
CREATE TABLE #i (id INT primary key) INSERT INTO #i SELECT 405782
SELECT 'value' as test
When I try them in SQL Server Mangement Studio, both work fine and return one row. When I try the first one from PHP, it works fine and returns one row. However, when I try to execute the second query from PHP I get an unexpected result:
$SQL_query= '********'; //Second query
$serverName = '**********';
$connectionInfo = array( "Database"=>"*****","UID"=>"********","PWD"=>"*********");
$conn = sqlsrv_connect( $serverName, $connectionInfo);
$msg1=sqlsrv_errors(SQLSRV_ERR_ALL); // ""
if($conn){ //truthy
$result=sqlsrv_query($conn,$SQL_query);
if(sqlsrv_has_rows ($result)){$rows='true';}else{$rows='false';} //false
$msg2=sqlsrv_errors(SQLSRV_ERR_ALL); // ""
$row=sqlsrv_fetch_array($result,SQLSRV_FETCH_NUMERIC) //false
}
(The odbc functions were even worse, choking if the query contained a SET #var statement...)
So the result of the query is incorrect, but no errors are reported.
Can anyone explain this? You'd think if the range of queries that could be handled by these functions was somehow limited that it would be at least mentioned in passing in the PHP documentation for these functions.
For reference: Microsoft SQL Server Standard Edition 9.00.1406.00, PHP 5.5.19 x86 thread safety disabled, running on Windows.
Edit: Per Rob Farley's suggestion, I've confirmed that the ##OPTIONS are either identical or immaterial to reproducing the problem.
Depending on what driver you're using (freetds?), you could be finding that ANSI_NULLS (and other ANSI settings, such as QUOTED_IDENTIFIER) are set differently to what is expected, and this can affect things such as the creation of tables. Try passing in a query that tests those values, and you will quite probably find the problem.
The problem is that the client framework is confused by the row counts that are returned by default for every statement. When the first INSERT happens, it returns a row count of 1, making it appear as if this is a statement returning results, which it's not -- the row count may even get confused for the result itself. PHP isn't the only technology troubled by this; jTDS is another driver that has trouble (in the case of jTDS, it requires the row count). SET NOCOUNT ON suppresses this extra information:
SET NOCOUNT ON
IF OBJECT_ID('tempdb..#i') IS NOT NULL BEGIN DROP TABLE #i END
CREATE TABLE #i (id INT primary key)
INSERT INTO #i SELECT 405782
SELECT 'value' as test

php pdo fetchAll returns SQL error 'no active fields' when executing stored procedure

My stored procedure executes 100% fine from the Management Studio, but when running through PDO with a try catch block I get the following exception message:
SQLSTATE[IMSSP]: The active result for the query contains no fields.
I have tried the classic SET NOCOUNT ON (this caught me out previously) to stop it returning row counts, and I've done various tests by removing sections of the SP until I have found which section the error lies in. I've also tried the PHP PDO nextRowset() with no luck.
The Stored Procedure:
I declare a cursor (shock, horror I know!) and iterate over some results, which itself caused no issues - but in reality this cursor must run various stored procedures itself for each fetch in the cursor, and when I introduce these stored procedures that is when the issues appear.
I have gone through the SPs inside the cursor and SET NOCOUNT ON on them in case that might be the issue, but no luck. One or two of these SPs have OUTPUTS but these are captured in variables accordingly.
Does anyone have any ideas? I don't wish to post any code of the project but some scenarios of commands I perform in the cursor block:
SELECT #varName = columnName FROM dbo.tableName
SET #varName = (SELECT columnName FROM dbo.tableName)
EXEC dbo.storedProcedure #outputVar OUTPUT
My best guess is the top example is the problem, but I am not knowledgeable to know. I would like to locate the error without removing these one by one as the actions performed by the procedure as a whole are difficult to roll-back on my test database and each line is important to getting correct output.
Thanks in advance for any help provided!
I have now managed to solve this! Hopefully the solution will help others who run into this error or similar.
The issue was that nested cursors (cursor inside cursor) were in use for the SQL Server stored procedure. My main procedure (called from PHP by PDO) opened a cursor and then ran other stored procedures inside that cursor that opened a cursor of their own.
This method works fine when running the query in SQL Server Management Studio, but calling from PHP via PDO fails.
While I know that using cursors is considered bad practice by most SQL buffs, unfortunately I inherited these stored procedures so I'm removing all blame from myself!
The actual solution was to replace the cursor in the originating stored procedure (the one called by PHP which in turn calls the other SPs) with a while loop using code like this:
DECLARE #loopTable table (id int IDENTITY(1,1), dataColumn)
DECLARE #id int
DECLARE #rows int
DECLARE #data int -- var to hold targeted data in the loop
INSERT INTO #loopTable (dataColumn)
SELECT dataColumn FROM dataTable
SELECT #rows = COUNT(1) FROM #loopTable
WHILE (#rows > 0)
BEGIN
SELECT TOP 1 #data = dataColumn, #id = id FROM #loopTable
// Do stuff with #data variable here
DELETE FROM #loopTable where id = #id
SELECT #rows = COUNT(1) from #loopTable
END
Problem solved, nightmare to find!

Can I use the result of a previous MySQL query in the From section of another MySQL query?

I'm using a PHP webservice where I have performed a simple SELECT query, and stored it
$result = run_query($get_query);
I now need to perform further querying on the data based on different parameters, which I know is possible via MySQL in the form:
SELECT *
FROM (SELECT *
FROM customers
WHERE CompanyName > 'g')
WHERE ContactName < 'g'
I do know that this performs two Select queries on the table. However, what I would like to know is if I can simply use my previously saved query in the FROM section of the second section, such as this, and if my belief that it helps performance by not querying the entire database again is true:
SELECT *
FROM ($result)
WHERE ContactName < 'g'
You can make a temp table to put the initial results and then use it to select the data and in the second query. This will work faster only if your 1-st query is slow.
PHP and SQL are different languages and very different platforms. They often don't even run in the same computer. Your PHP variables won't interact at all with the MySQL server. You use PHP to create a string that happens to contain SQL code but that's all. In the end, the only thing that counts is the SQL code you sent to the server—how you manage to generate it is irrelevant.
Additionally, you can't really say how MySQL will run a query unless you obtain an explain plan:
EXPLAIN EXTENDED
SELECT *
FROM (SELECT *
FROM customers
WHERE CompanyName > 'g')
WHERE ContactName < 'g'
... but I doubt it'll read the table twice for your query. Memory is much faster than disk.
Thanks for the responses, everyone. Turns out what I was looking for was a "query of query", which isn't supported directly by PHP but I found a function over here which provides the functionality: http://www.tom-muck.com/blog/index.cfm?newsid=37
That was found from this other SO question: Can php query the results from a previous query?
I still need to do comparisons to determine whether it improves speed.
If I understand your question correctly you want to know whether saving the "from" part of your SQL query in a php variable improves the performance of you querying your SQL server, then the answer is NO. Simply because the variable keeping the value is inserted into the query.
Whether performance is gained in PHP, the answer is most probable yes; but depends on the length of the variable value (and how often you repeat using the variable instead of building a new complete query) whether the performance will be notable.
Why not just get this data in a single query like this?
SELECT *
FROM customers
WHERE CompanyName > 'g'
AND ContactName < 'g'

PHP or MYSQL not recognizing value

I'm using php to query from mysql in which either the home_team or away_team column contains a given value. I know that records of such values exist in the DB. But, for some reason either PHP or MySQL is telling me that it's not. My code is as follows.
$teams = array("St. Louis",
"NY Yankees",
"NY Mets",
"LA Dodgers",
"LA Angels");
foreach($teams as $given_team)
{
$query = mysql_query("SELECT COUNT(*) FROM current_season_games WHERE home_team = '".$given_team."' OR away_team = '".$given_team."'")or die(mysql_error());
$count = mysql_result($query, 0);
echo "".$count."<br />";
}
I know that the $count variable that's being echoed is incorrect. I'm looking at some rows in the DB that fit the conditions specified in the query.
Any ideas?
Thanks,
Lance
You are using COUNT(*). This won't give you the value of the field. COUNT(*) is usually used to measure how many results are returned.
What you'll want to do is actually select the column name or alternatively, select the entire row that matches the criteria. Something like this -
SELECT * FROM current_season_games
WHERE home_team = '".$given_team."' OR away_team = '".$given_team."'")
Here are a couple of things to check:
is PHP connecting to the same host and database? (This has been a 'DOH!' moment for some developers, when they have multiple databases... one local, and one on a test server.) Are you sure you are querying the same table?
try testing a simpler query, e.g. SELECT COUNT(*) FROM current_season_games to see if you can get any non-zero count returned
try putting the query into a string, and echoing out the string, before you execute it
is this an InnoDB table, and have the rows you are trying to count been committed? (Did the session adding the rows do a START TRANSACTION, insert the rows, and then not yet commit them? (This is a DOH! moment for some developers that are using multiple environments (e.g. SQLyog and a web server). (A separate session with transaction isolation level of REPEATABLE READ would not see the uncommitted rows)
is that column in the database using a case sensitive collation, where the equality comparison in the predicates (WHERE clause) is being handled as case sensitive vs. case insensitive?
None of those may solve your problem, but it's impossible to tell (from here), without a lot more information, what the issue is. But it's a few things you can check.
In old code that I come across still using the deprecated mysql_* functions, I'll usually come across the use of the mysql_num_rows() function.
Retrieves the number of rows from a result set. This command is only valid for statements like SELECT or SHOW that return an actual result set. To retrieve the number of rows affected by a INSERT, UPDATE, REPLACE or DELETE query, use mysql_affected_rows().
I would recommend changing your code to something like this -
$result = mysql_query("SELECT COUNT(*) FROM current_season_games WHERE home_team = '".$given_team."' OR away_team = '".$given_team."'")or die(mysql_error());
$count = mysql_num_rows($result);
Notice that the mysql_query() function doesn't return a $query as your variable name indicates. It returns a result set that you can then inspect to retrieve that actual data.

MSSQL_query in PHP Returning Diffrent Results than SQL Native Sproc

Anyone ran into this one before?
I have a Stored Procedure in SQL with the following Parameters :
exec PaySummary 'DemoTest', 'DemoTest-Mohn-00038', '5/14/12', '5/27/12', 'manager', 'DemoTest-Boyd-00005'
And the following MSSQL Query in PHP running the exact same query.
private function dataTest(){
$strSQL = 'exec PaySummary \'DemoTest\', \'DemoTest-Mohn-00038\', \'5/14/12\', \'5/27/12\', \'manager\', \'DemoTest-Boyd-00005\'';
$a = mssql_query($strSQL);
echo $strSQL;
while($row=mssql_fetch_array($a)){
var_dump($row);
}
}
When run in SQL for this query I will get 3 results...
When run in PHP through SQL I get 2 Results...
Is there any run time settings (Set NoCOUNT on) that you must set on a SQL Stored Procedure to ensure accuracy of the output of results? Or is there a known issue with passing date parameters that would impact the results of a date driven stored procedure?
Microsoft-IIS/5.0 / PHP/5.2.5 / SQL Server 2008 R2 (Where the stored procedure is executed).
For anyone in this same situation... It is caused by the NULL_CONCAT_NULL (or whatever) option in SQL. This one flag can make a stored procedure run a little bit differently depending on how you use concat etc. A good way to solve this problem is via an ISNULL around a lot of your items which seemed to get rid of the issue of getting different results.
Further another option if you do not want to fix your sprocs is to check the path that sql is going through (TCP/IP etc). I noticed when watching the audits that some settings were wildly different depending on the port that sql was running through.

Categories