php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #54444 Multiple Queries on a single conenction
Submitted: 2011-04-01 16:13 UTC Modified: 2011-05-13 11:44 UTC
From: peter dot colclough at toolstation dot com Assigned:
Status: Not a bug Package: MySQLi related
PHP Version: 5.3SVN-2011-04-01 (SVN) OS: Ubuntu 10 64 bit
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: peter dot colclough at toolstation dot com
New email:
PHP Version: OS:

 

 [2011-04-01 16:13 UTC] peter dot colclough at toolstation dot com
Description:
------------
Hi, trying to build a generic DB object handler for mySqli, and have hit an issue where we can't have more than one open query on the same connection. Is this a bug or 'expected behaviour'?
Looking at the mysqli.c source code, it looks like it should have been possible, but it looks like the second object overwrites the first...

I have put a sample snippet below of what I am trying to achieve.... if this helps.

Any help greatly appreciated....

OS: 2.6.32-21-generic #32-Ubuntu SMP Fri Apr 16 08:09:38 UTC 2010 x86_64 GNU/Linux
PHP Version => 5.3.2-1ubuntu4.5

	


Test script:
---------------
-------------- Code Snippet -----------------------------
$sqlstock = 'select foo1 from bar1 where foo1 =?';
$sqltime  = 'select foo2, foo3 from bar2 where foo4 =?';

$varinp = "XXXXXXXXXXXX";
$abindVars = array(0=>$varinp);
	
$varProd = '';
$conn = dbi->db_conn;
$sprod = '';
$timestart = microtime_float();
	
// Get a statement
$aRes = array();
$aRes2 = array();

// Init 2 Statements	
$stmt  = mysqli_stmt_init($conn);
$stmt2  = mysqli_stmt_init($conn);
	
// Prepare 2 statements
mysqli_stmt_prepare($stmt,$sqlstock);
mysqli_stmt_prepare($stmt2,$sqltime);
	
// Set the bind variable
$varinp = "PXX00019263";
	
// Bind the statements	
mysqli_stmt_bind_param($stmt,'s', &$varinp);
mysqli_stmt_bind_param($stmt2,'s', &$varinp);
	
// Execute - Second one fails	
mysqli_stmt_execute($stmt);
mysqli_stmt_execute($stmt2);
	
// Set up field Defs
$aFieldDefs = array();
$aFieldDefs2 = array();
	
// Get result Metadata
$result = mysqli_stmt_result_metadata($stmt);
$result2 = mysqli_stmt_result_metadata($stmt2);
	

$nCount = 0;
while($aFieldDefs[$nCount] = mysqli_fetch_field($result)){
        echo('Field = '.print_r($aFieldDefs, true)."\r\n");
	$aRes[$aFieldDefs[$nCount++]->name] = null;
}

$nCount = 0;
while($aFieldDefs2[$nCount] = mysqli_fetch_field($result2)){
	echo('Field = '.print_r($aFieldDefs2, true)."\r\n");
	$aRes2[$aFieldDefs2[$nCount++]->name] = null;
}
	
// Bind Results
mysqli_stmt_bind_result($stmt, &$aRes['foo1']);
mysqli_stmt_bind_result($stmt2, &$aRes2['foo2'], &$aRes2['foo3'])
// Fetch Results
mysqli_stmt_fetch($stmt);
mysqli_stmt_fetch($stmt2);
	
echo(print_r($aRes, true)."\r\n");
echo(print_r($aRes2, true)."\r\n");
--------------- End Code Snippet ----------------------

Expected result:
----------------
Array
(
    [foo1] => 'PXX00019263'
)

Array
(
    [foo2] => 2009-09-15 12:05:02
    [foo3] => 0000-00-00 00:00:00
)


Actual result:
--------------
Array
(
    [foo1] => 
)

Array
(
    [foo2] => 2009-09-15 12:05:02
    [foo3] => 0000-00-00 00:00:00
)


Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2011-05-09 16:14 UTC] uw@php.net
-Status: Open +Status: Bogus
 [2011-05-09 16:14 UTC] uw@php.net
You can answer this question yourself by adding a bit of error handling to your script such as ...

$mysqli = new mysqli("localhost", "root", "root", "test");
$stmt1 = $mysqli->prepare("SELECT 1 AS _one FROM DUAL");
$stmt2 = $mysqli->prepare("SELECT 2 AS _two FROM DUAL");

if (!$stmt1->execute() || !($meta1 = $stmt2->result_metadata()))
  printf("[001] [%d] %s\n", $stmt1->errno, $stmt1->error);

if (!$stmt2->execute() || !($meta2 = $stmt2->result_metadata()))
  printf("[002] [%d] %s\n", $stmt2->errno, $stmt2->error);


... and the answer is:

[002] [2014] Commands out of sync; you can't run this command now
 [2011-05-09 16:35 UTC] peter dot colclough at toolstation dot com
Thanks for teh feedback. I was also getting that error, just wanted to make sure it wasn't 'me'... but actually expected behaviour.

Am now devbeloping my own, that allows multiple statements per connection, as well as multiple 'prepare' statements. This will be open sourced when ready.

The current mysqli interface should have been able to do this, but it was obviously decided not to allow it... which is a bit of a pain.

Thanks again for your input
 [2011-05-09 17:00 UTC] uw@php.net
Hmm, you can prepare as many statements as you want per connection. But once you have executed a statement on a connection you must fetch its results before you can execute another statement. The result set "blocks" the line. 

You can, of course, do an implicit fetch on the C level upon execute but that's the exact opposite of the default fetch method (unbuffered) used for prepared statements by MySQL. 

Its a one-liner to do that fetch in user land. No need for changes on the C level.

$mysqli = new mysqli("localhost", "root", "root", "test");
$stmt1 = $mysqli->prepare("SELECT 1 AS _one FROM DUAL");
$stmt2 = $mysqli->prepare("SELECT 2 AS _two FROM DUAL");

/* execute */
if (!$stmt1->execute())
  printf("[001] [%d] %s\n", $stmt1->errno, $stmt1->error);

/* clear line by fetching result set */
$res1 = $stmt1->get_result();


/* execute */
if (!$stmt2->execute())
  printf("[002] [%d] %s\n", $stmt2->errno, $stmt2->error);

/* clear line by fetching result set */
$res2 = $stmt2->get_result();

/* fetching second first */
while ($row = $res2->fetch_assoc())
  var_dump($row);
$res2->free();

while ($row = $res1->fetch_assoc())
  var_dump($row);
$res1->free();
 [2011-05-10 12:01 UTC] peter dot colclough at toolstation dot com
True... but it doesn't do this ($conn is a connection) ... and don't copy this code.. its not good.. but gets the point across:


$basesql = 'SELECT SQL_NO_CACHE id from table1 ';
$baseqry = bmysqli_query($basesql, $conn);

$qry  = bmysqli_query('Select SQL_NO_CACHE * from table2 where id2 = ? ',$conn);
$qry1 = bmysqli_query('Select SQL_NO_CACHE * from table3 where id3 = ? ',$conn);
$qry2 = bmysqli_query('Select SQL_NO_CACHE * from table4 where id4 = ? ',$conn);

while(TRUE){
   $nBaseRes = bmysqli_fetch($baseqry);
   if(empty($nBaseRes)){
       break;
   }
   $aId      = array(0 =>$nBaseRes['id']);
   // The first bound query
   bmysqli_execute($qry, $aId);
   $nRes  = bmysqli_fetch($qry);
   $aVals = array(0=>$nRes['id2']);

   bmysqli_execute($qry1, $aVals);	

    while(TRUE){			// careful Peter
	$nRes1 = bmysqli_fetch($qry1);
	if(empty($nRes1)){
		break;
	}
	
        $aItems = array(0 => $nRes1['id3']);

	bmysqli_execute($qry2, $aItems);

	while(TRUE){			// careful Peter
       	   $nRes2 = bmysqli_fetch($qry2);
	   if(empty($nRes2)){
			break;
	   }
	
			
			
	}
	
	
}
 [2011-05-11 11:12 UTC] johannes@php.net
The server won't process a second query unless all data from the previous has been fetched. As Ulf said you can buffer the result set locally so you can send the next query. We don't do this intentionally by default as loading result sets in memory can be very hard when they are large.
 [2011-05-13 09:59 UTC] peter dot colclough at toolstation dot com
Johannes,

I accept what you, and Ulf, are saying, however.... do you think it right that the language shouold dictate what the application developer can do? While it would be really dumb to have 6 x 10 gig results stored on the server, it may be a necessity, and memory is not a main issue these days. Its up to the app developer how they store the data, and where it is stored mid query.
We are processing, on average, 1000 queries a second over any 24 hr period. Some of those have complex joins, which in turn cause table locks. By using multiple prepared statements, we can get around that issue, and speed up total throughput.

Will let you know when the driver is available in alpha... so you can pass comments, if you are interested.
 [2011-05-13 11:44 UTC] johannes@php.net
You can use mysqli_store_result or mysqli_fetch_all to fetch the result set into local memory. And in a system with 100 or so PHP processes in parallel memory is still an issue. You are free to do whatever you want, there's no limitation. The main thing is that after bad experience with the mysql_query() behaviour we've changed the default with mysqli for being unbuffered. (To have this more CS like: The unbuffered approach let's you write applications with complexity O(n), with buffered the same thing will be O(2n) )
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Sat Dec 21 17:01:58 2024 UTC