My server runs CentOS 6.4 with MySQL 5.1.69 installed using yum with CentOS's repos, and PHP 5.4.16 installed using yum with ius's repos. Edit3 Upgraded to MySQL Server version: 5.5.31 Distributed by The IUS Community Project, and error still exists. Then changed library to mysqlnd, and seems to eliminate the error. Still, with this back and forth, need to know why this error only sometimes manifests.
When using PDO and creating the PDO object using PDO::ATTR_EMULATE_PREPARES=>false, I sometimes get the following error:
Table Name - zipcodes
Error in query:
SELECT id FROM cities WHERE name=? AND states_id=?
SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.
File Name: /var/www/initial_install/build_database.php
Line: 547
Time of Error: Tuesday July 2, 2013, 5:52:48 PDT
Line 547 is the last line of:
$stmt_check_county->execute(array($data[5],$data[4]));
if(!$county_id=$stmt_check_county->fetchColumn())
{
$stmt_counties->execute(array($data[5]));
$county_id=db::db()->lastInsertId();
}
//$stmt_check_county->closeCursor(); //This will fix the error
$stmt_check_city->execute(array($data[3],$data[4]));
I had a similar problem several years ago, but upgraded from PHP 5.1 to PHP 5.3 (and MySQL probably was updated as well), and the problem magically went away, and now I have it with PHP 5.5.
Why does it only manifest itself when PDO::ATTR_EMULATE_PREPARES=>false, and with only alternating version of PHPs?
I've also found that closeCursor() will also fix the error. Should this always be done after every SELECT query where fetchAll() is not used? Note that the error still occurs even if the query is something like SELECT COUNT(col2) which only returns one value.
Edit By the way, this is how I create my connection. I've only recently added MYSQL_ATTR_USE_BUFFERED_QUERY=>true, however, it doesn't cure the error. Also, the following script could be used as is to create the error.
function sql_error($e,$sql=NULL){return('<h1>Error in query:</h1><p>'.$sql.'</p><p>'.$e->getMessage().'</p><p>File Name: '.$e->getFile().' Line: '.$e->getLine().'</p>');}
class db {
private static $instance = NULL;
private function __construct() {} //Make private
private function __clone(){} //Make private
public static function db() //Get instance of DB
{
if (!self::$instance)
{
//try{self::$instance = new PDO("mysql:host=localhost;dbname=myDB;charset=utf8",'myUsername','myPassword',array(PDO::ATTR_EMULATE_PREPARES=>false,PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC));}
try{self::$instance = new PDO("mysql:host=localhost;dbname=myDB;charset=utf8",'myUsername','myPassword',array(PDO::ATTR_EMULATE_PREPARES=>false,PDO::MYSQL_ATTR_USE_BUFFERED_QUERY=>true,PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC));}
//try{self::$instance = new PDO("mysql:host=localhost;dbname=myDB;charset=utf8",'myUsername','myPassword',array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC));}
catch(PDOException $e){echo(sql_error($e));}
}
return self::$instance;
}
}
$row=array(
'zipcodes_id'=>'55555',
'cities_id'=>123
);
$data=array($row,$row,$row,$row);
$sql = 'CREATE TEMPORARY TABLE temp1(temp_id INT UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY (temp_id) )';
db::db()->exec($sql);
$sql='SELECT COUNT(*) AS valid FROM cities_has_zipcodes WHERE cities_id=? AND zipcodes_id=?';
$stmt1 = db::db()->prepare($sql);
$sql ='SELECT temp_id FROM temp1';
$stmt2 = db::db()->prepare($sql);
foreach($data AS $row)
{
try
{
$stmt1->execute(array($row['zipcodes_id'],$row['cities_id']));
$rs1 = $stmt1->fetch(PDO::FETCH_ASSOC);
//$stmt1->closeCursor();
syslog(LOG_INFO,'$rs1: '.print_r($rs1,1).' '.rand());
$stmt2->execute();
$rs2 = $stmt2->fetch(PDO::FETCH_ASSOC);
syslog(LOG_INFO,'$rs2: '.print_r($rs2,1).' '.rand());
}
catch(PDOException $e){echo(sql_error($e));}
}
echo('done');
The MySQL client protocol doesn't allow more than one query to be "in progress." That is, you've executed a query and you've fetched some of the results, but not all -- then you try to execute a second query. If the first query still has rows to return, the second query gets an error.
Client libraries get around this by fetching all the rows of the first query implicitly upon first fetch, and then subsequent fetches simply iterate over the internally cached results. This gives them the opportunity to close the cursor (as far as the MySQL server is concerned). This is the "buffered query." This works the same as using fetchAll(), in that both cases must allocate enough memory in the PHP client to hold the full result set.
The difference is that a buffered query holds the result in the MySQL client library, so PHP can't access the rows until you fetch() each row sequentially. Whereas fetchAll() immediately populates a PHP array for all the results, allowing you access any random row.
The chief reason not to use fetchAll() is that a result might be too large to fit in your PHP memory_limit. But it appears your query results have just one row anyway, so that shouldn't be a problem.
You can closeCursor() to "abandon" a result before you've fetched the last row. The MySQL server gets notified that it can discard that result on the server side, and then you can execute another query. You shouldn't closeCursor() until you're done fetching a given result set.
Also: I notice you're executing your $stmt2 over and over inside the loop, but it will return the same result each time. On the principle of moving loop-invariant code out of the loop, you should have executed this once before starting the loop, and saved the result in a PHP variable. So regardless of using buffered queries or fetchAll(), there's no need for you to nest your queries.
So I would recommend writing your code this way:
$sql ='SELECT temp_id FROM temp1';
$stmt2 = db::db()->prepare($sql);
$stmt2->execute();
$rs2 = $stmt2->fetchAll(PDO::FETCH_ASSOC);
$stmt2->closeCursor();
$sql='SELECT COUNT(*) AS valid FROM cities_has_zipcodes
WHERE cities_id=:cities_id AND zipcodes_id=:zipcodes_id';
$stmt1 = db::db()->prepare($sql);
foreach($data AS $row)
{
try
{
$stmt1->execute($row);
$rs1 = $stmt1->fetchAll(PDO::FETCH_ASSOC);
$stmt1->closeCursor();
syslog(LOG_INFO,'$rs1: '.print_r($rs1[0],1).' '.rand());
syslog(LOG_INFO,'$rs2: '.print_r($rs2[0],1).' '.rand());
}
catch(PDOException $e){echo(sql_error($e));}
}
Note I also used named parameters instead of positional parameters, which makes it simpler to pass $row as the array of parameter values. If the keys of the array match the parameter names, you can just pass the array. In older versions of PHP you had to include the : prefix in the array keys, but you don't need that anymore.
You should use mysqlnd anyway. It has more features, it's more memory-efficient, and its license is compatible with PHP.
I am hoping for a better answer than the following. While some of these solutions might "fix" the problem, they don't answer the original question regarding what causes this error.
Set PDO::ATTR_EMULATE_PREPARES=>true (I don't wish to do this)
Set PDO::MYSQL_ATTR_USE_BUFFERED_QUERY (didn't work for me)
Use PDOStatement::fetchAll() (not always desirable)
Use $stmt->closeCursor() after each $stmt->fetch() (this mostly worked, however, I still had several cases where it didn't)
Change PHP MySQL library from php-mysql to php-mysqlnd (probably what I will do if no better answer)
I have almost same problem. My first query after connection to db return empty result and drop this error. Enabling buffer doesn't help.
My connection code was:
try {
$DBH = new PDO("mysql:host=$hostname;dbname=$db_name", $username, $password,
array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET CHARACTER SET utf8; SET NAMES utf8",
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_NUM));
}
catch(PDOException $e) { echo $e->getMessage(); }
Solution in my way was to remove initial command:
PDO::MYSQL_ATTR_INIT_COMMAND => "SET CHARACTER SET utf8; SET NAMES utf8"
Here is a correct code:
try {
$DBH = new PDO("mysql:host=$hostname;dbname=$db_name", $username, $password,
array(PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_NUM));
}
catch(PDOException $e) { echo $e->getMessage(); }
And MYSQL_ATTR_USE_BUFFERED_QUERY is not forced to true. It's set as default.
I also experienced this problem today and noticed that I put wrong SQL statement (SELECT) into PDO's exec() method. Then I came to a conclusion that we can only put write (INSERT, UPDATE, DELETE) SQL statements instead of read (SELECT) ones to the method.
!!!
WARNING !!!
This can also happen if you are trying to fetch a non SELECT query (Eg - UPDATE/INSERT/ALTER/CREATE)
if anybody is here, with error while creating tables
this also happens if you try to execute create 2 tables in single query;
this error was thrown when i fired below query;
$q24="create table if not exists table1 like template1;create table if not exists table2 like template2;";
$s24=$link->prepare($q24);
$s24->execute();
seems tables are to be created separately;
$q1="create table if not exists table1 like template1;";
$s1=$link->prepare($q1);
$s1->execute();
//and
$q2="create table if not exists table2 like template2;";
$s2=$link->prepare($q2);
$s2->execute();
I had the same problem, I was sending results to another function mid loop. Quick fix was, save all results in an array (like Bill stated, if it's too large you have other issues to worry about), after collecting the data, I ran a separate loop to call the function one at a time.
Also, PDO::MYSQL_ATTR_USE_BUFFERED_QUERY did not work for me.
I had the same problem and solved it by removing all initial requests related to the character set.
so I started from
$con = new \PDO(self::getDriver() . ":host=" . self::getHost() . ":".self::getPort()."; dbname=" . self::getName() . ";charset=utf8", self::getUser(), self::getPassword(), array( \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,\PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8;SET SESSION time_zone ='+01:00'"));
to
$con = new \PDO(self::getDriver() . ":host=" . self::getHost() . ":".self::getPort()."; dbname=" . self::getName() . ";charset=utf8", self::getUser(), self::getPassword(), array( \PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true,\PDO::MYSQL_ATTR_INIT_COMMAND => "SET SESSION time_zone ='+01:00'"));
so I removed the command SET NAMES utf8;
I got this error when I accidentally called execute twice, one explicitly and one that was hidden in another call (PDO class).
After removing the first execute, the error was gone.
The main reason behind this error is that MySQL is trying to run 'exec' instead of 'execute' and vice versa.
There are two PDO statements that are there to execute queries PDO::exec() and PDO::execute(), both are not the same.
PDO::exec() is designed to execute commands and queries that do not produce a result set.
Ex: SET, UPDATE, INSERT, DELETE etc.
PDO::execute() is designed to execute commands and queries that produce a result set.
Ex: SELECT, CALL, SHOW, OPTIMIZE, EXPLAIN etc.
If you use these commands in wrong place, your will be ended up with this error.
Solution:
Be careful where to use PDO::exec() and PDO::execute()
In my case for Laravel I changed my query from
DB::select("DELETE FROM " . env('DB_PREFIX') . 'products WHERE
product_id = ' . $product->id); // Internally Laravel will run PDO::execute()
'select' method
to
DB::table('product_currency')->where('product_id',
$product->id)->delete(); // Internally Laravel will run PDO::exec()
Hope this gives some more clarification!
I am working on a user login feature and facing a strange problem "Invalid Number of Columns". I Google'd it and many people have the same issue and their question was bit different.
Here is my code:
//this is a function inside user class. And function receives $user_data array
try{
$stmt = $this->db_connection->prepare("SELECT `id` FROM `aaa_users` WHERE (`user_email` = :user_email OR `user_name` = :user_email) AND `user_pass` = :user_pass");
$stmt->bindparam(':user_email', $user_data['email']);
$stmt->bindparam(':user_pass', $user_data['password']);
$stmt->execute();
$count = $stmt->rowCount();
} catch (PDOException $e){
echo $e->getMessage();
}
The registration query works but this throws an exception. I wonder if there might be tiny mistake but I can't figure it out.
As I mentioned in comments, PDO's emulation may not be enabled on your server and using the same named placeholder (may be) causing this to error out.
I've respectively renamed both :user_mail to :user_email_1 and :user_email_2.
$stmt = $this->db_connection->prepare("
SELECT `id` FROM `aaa_users`
WHERE (`user_email` = :user_email_1
OR `user_name` = :user_email_2)
AND `user_pass` = :user_pass
");
$stmt->bindparam(':user_email_1', $user_data['email']);
$stmt->bindparam(':user_email_2', $user_data['email']);
$stmt->bindparam(':user_pass', $user_data['password']);
$stmt->execute();
You can read more about this in the following Q&A on Stack:
Support server side prepared statements with PDO?
The accepted answer inside it, offers a good explanation.
Note: Make sure that both all values related to $user_data['X_values'] contain value. This suggests that it may be coming from a previous query and is unknown as to their origins / values.
Use error checking by following the below links, if any of them have not already been used during testing:
http://php.net/manual/en/pdo.error-handling.php
http://php.net/manual/en/function.error-reporting.php
While making sure that all columns do in fact exist. There could be the slightest chance that your database/table stand to be case-sensitive, so check for that (letter case) also.
Another thing; it has happened in the past where people actually had to use bindParam in Camel Case instead of bindparam all in lower case; it's a possibility.
I am attempting to get the following PDO statement to work and running into issues. When I am trying to get the number of rows, I keep getting 0, yet I know there should be 1 row. When I ran it as a mysqli statement( before trying to change it to PDO) it worked perfectly.
Here is the code:
require_once ('pdo.php');
$isbn = $_POST['isbn'];
// check to see if the isbn is a "problem" isbn or not
$problem = $conn->prepare("select isbn, note from problem where isbn = :isbn");
$problem->bindParam(":isbn", $isbn);
$problem->execute();
print_r($problem);
$num_rows = $problem->rowCount();
print_r($num_rows); die;
EDIT: Here is pdo.php:
<?php
function db_connect()
{
$db = new PDO("mysql:host=localhost; db=bookcell_BCOS_final", "xxxxx", "xxxxx");
return($db);
}
?>
I know that my connection works, but I get 0 for $num_rows. What mistakes am I making here?
Besides a little quirk and a optimalisation your code looks fine to me. The posted value isbn could be the reasong that you are getting no data:
$problem = $conn->prepare("select isbn, note from problem where isbn = :isbn");
$problem->bindParam(":isbn", $_POST['isbn'], PDO::PARAM_STR); // <-- thats what parameter binding is for
$problem->execute();
print_r($problem);
$num_rows = $problem->rowCount(); // <-- gives the number of rows, not columnCOunt
print_r($num_rows); die;
The Syntax for $num_rows = $problem->columnCount(); is totally correct. You may try,
$problem->execute(array("isbn" => $isbn));
instead of bindParam.
for getting the no. of rows, you need to use pdo::rowCount() -- manual here
In PDO to verfiy if your execute statement did work, check the return value (bool):
$success = $problem->execute();
if (!$success) {
$arr = $problem->errorInfo();
print_r($arr);
}
Also you might be looking for rowCount() instead of columnCount() but I think the error handling is your furthermost issue.
Additionally you can make PDO throw an exception each time an error appears, compare:
Switching from PHP's mysql extension to PDO. Extend class to reduce lines of code
How do I raise PDOException?
Depending on the database driver and the mode it's running, PDO may not be able to give you a row count. Look carefully at the documentation for PDOStatement::rowCount():
If the last SQL statement executed by the associated PDOStatement was a SELECT statement, some databases may return the number of rows returned by that statement. However, this behaviour is not guaranteed for all databases and should not be relied on for portable applications.
This is because in many cases the database uses a cursor rather than fetching the full results and buffering them (which is how the old mysql_* functions behave). In this case the database doesn't know how many rows there are until you have looked at all the rows. Think of a cursor as something like a filesystem pointer--you can't know the filesize until you seek to the end of the file.
I have been converting a lot of my old MySQL stuff to MySQLi in PHP and am getting a problem on the following code:
### FETCH INCLUDES ###
$player=$_POST['player'];
$password=md5($_POST['password']);
#### DB CONNECTION ####
if(!$mysqli=new mysqli(DBHOST,DBUSER,DBPWD,DBNAME)) {$err=$mysqli->error; print($err); }
$sql="SELECT * FROM accounts WHERE name='?' AND passkey='?'";
if($stmt=$mysqli->prepare($sql)) {
//$stmt->bind_param('ss',$player,$password);
$stmt->execute();
$stmt->store_result();
if($stmt->num_rows==1) {
$account=$stmt->fetch_assoc();
// purely for debugging
print_r($account);
if($_SESSION['account']=$account) $account=true;
} else {
echo "Failed. Row count: ";
print($stmt->num_rows);
echo "<br />";
$query=str_replace('?','%s',$sql);
printf($query,$player,$password);
$account=false;
}
$stmt->close();
} else {
$err=$mysqli->error;
print($err);
}
I have narrowed down the fault to the query itself. I am getting 0 rows returned, no errors, so I thought I would output the query (the str_replace thing I have going there) and I can use the query to return a row from the database using the same query from PHPMyAdmin
Where am I going wrong?
EDIT
I tried changing the query to a basic one without binding params - "SELECT * FROM table"
still get no rows returned. So it is isn't the query itself, it would be something in my order/format of the prepare,execute situation
second edit: I have added the $stmt->store_result() to the code and still returns 0 row count.
Third Edit:
I investigated the connection and user settings which seem fine. I can connect via console to the database using the same user and password, and the database name is the same. I am really stumped on this :(
Add a $stmt->store_result(); after $stmt->execute();, as it seem's it must be called once before $stmt->num_rows... At least they do this in the examples (see http://php.net/manual/en/mysqli-stmt.store-result.php). And they meantion a dependency in the documentation of "num_rows".
Other ideas: You check for if($stmt->num_rows==1) {, are you sure num_rows is 0? I don't know your database structure for the table "accounts". Is "name" the primary key (or at least a unique index)? If not, there could be multiple columns that match. That's just a quick idea what could be wrong, and cause you looking hours for the problem in your source code. While the problem is somewhere else.
Ok, I did check your code. First you should fix your error handling when connecting. Don't check for "$mysqli" is true but check mysqli_connect_errno() like this:
$mysqli=new mysqli(DBHOST,DBUSER,DBPWD,DBNAME);
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
On my server the your code worked with correct credentials (e.g. DBHOST,DBUSER,DBPWD,DBNAME needed to be replaced).
And the statement $account=$stmt->fetch_assoc(); doesnt work. There is no fetch_assoc() function in the $stmt object. The fetch_assoc() is for mysqli::result objects obtained by using normal queries (not prepared statements). You need to use $stmt->bind_result(); and then $stmt->fetch(); Also you should to put a list of all column names in the query instead of "*", this defines a field order...
I got it working, I ended up needing the $stmt->store_result();
But I also noticed I had added single quotes around the ? in the prepare statement which was causing problems.
After taking those out and leaving in the store_result method it works!