ZF2 already active query prevents execution - php

I have something like the following, in a function that deletes both the files and db entries:
$adapter = $this->getAdapter();
$query = $adapter->query("call find_results_by_job_id(?)", array($jobId));
$items = array();
while (($current = $query->current()) !== false)
{
$id = $current['id'];
$items[] = $id;
$query->next();
}
$this->deleteFromDataStore($items);
$result = $adapter->query("call delete_results_by_job_id(?)", array($jobId), \Zend\Db\Adapter\Adapter::QUERY_MODE_EXECUTE);
(Some of that might not look like the best way to do it, because I simplified it for this example)
I'm getting this error on the last line: SQLSTATE[HY000]: General error: 2014 Cannot execute queries while other unbuffered queries are active.
I'm assuming that the problem is because the query/adapter hasn't closed the connection from iterating results yet when I try to execute another statement. If that is the case, how can reuse the adapter, or close the query, or whatever I have to do before the last line?
The strange part is that code following almost exactly this same pattern works in another method.
Answer:
When using the PDO driver, $query->getDataSource()->getResource()->closeCursor(); fixes it

Seems like you are using an unbuffered query in MySQL.
If it is so, you will either have to turn buffering on or break execution of previous query which seems to hang?
Something like $query->close()
EDIT:
If $query is instance of StatementInterface, then there is getResource() which returns mysqli_stmt and you can call close() on it.
EDIT2: (to incorporate final resolution)
In case it uses PDO, you can get PDOStatement and call closeCursor()

Assuming you have the result of a query in your hands and you dont know whether it is a ResultSet or a Result, the following will do the job.
Tested as of Zend Framework 3.
use Zend\Db\ResultSet\ResultSet;
...
public function closeResult( $result )
{
if( is_a($result, ResultSet::class) )
{
$stmt = $result->getDataSource()->getResource();
}
else
{
$stmt = $result->getResource();
}
$stmt->closeCursor();
}

$this->adapter
->query($sql)
->execute()
->getResource()
->fetchAll(\PDO::FETCH_ASSOC);

Related

mysqli error only on subsequent calls to same function - 'there is no next result set.'

I am iterating rows of a csv file.
On row 1, I call stored procedure (import_extended_data_sp) and it succeeds.
On row 2, the call fails with :
Strict Standards mysqli::next_result(): There is no next result set.
However, with the call being exactly the same as the first, I am struggling to see why ?
I have now hard coded test values as parameters, and checked that the Sproc has no issue with the same values being given twice.
It still fails on the second call !?
Am wondering if there is some nuance of mysqli, where I need to clear or reset something before making the second call ?
<?php include("cnn.php");?>
<?php include("fn_db.php");?>
# ... get csv file (skipped for brevity) #
while($row = fgetcsv($file_data))
{
$line = array_combine($head, $row);
# This call works on every loop - no issues
$id = placemark_to_db($mysqli,$v_header,$line['id_placemark'],$line['name'],$line['swim_type'],$line['latitude'],$line['longitude'],$line['description']);
# This next line only succeeds on first call, but fails on next while loop
$x = xtended_to_db($mysqli,'99','[{"xtra":"oo"}]');
}
** fn_db.php >> xtended_to_db**
function xtended_to_db($cn,$id,$jsonarray){
# procedure returns a rowcount in output parameter
$cn->multi_query( "CALL import_extended_data_sp($id,'$jsonarray',#out);select #out as _out");
$cn->next_result();
$rs=$cn->store_result();
$ret = $rs->fetch_object()->_out;
$rs->free();
return $ret;
}
cnn.php
<?php
$mysqli = new mysqli("xxx.xxx.xxx.xxx","mydb","pass","user");
// Check connection
if ($mysqli -> connect_errno) {
echo "Failed to connect to MySQL: " . $mysqli -> connect_error;
exit();
}
?>
The best way to fix this error is to avoid multi_query() altogether. While it might sound like a reasonable use case with stored procedures, the truth is this function is mostly useless and very dangerous. You can achieve the same result using the normal way with prepared statements.
function xtended_to_db(mysqli $cn, $id, $jsonarray) {
$stmt = $cn->prepare('CALL import_extended_data_sp(?,?,#out)');
$stmt->bind_param('ss', $id, $jsonarray);
$stmt->execute();
$stmt = $cn->prepare('select #out as _out');
$stmt->execute();
$rs = $stmt->get_result();
return $rs->fetch_object()->_out;
}
If you are stuborn and you want to keep on using multi_query() then you need to be more careful with how you fetch results. This function is extremely difficult to get right. I am not going to show you how to fix multi_query() as I consider it too dangerous with variable input.
One last note, you really should think about getting rid of stored procedures. They are cumbersome and offer pretty much no benefit. There definitely is a better way to achieve what you want rather than calling stored procedure from PHP, but without seeing its contents I can't give you better advice.

Prepared statement returning False

I know there are many questions similar to my question. But I really can not figure the problem here. I have class named 'UsersClass' which is responsible on every task related to users. And I use mysqli prepare statement to insert or select data from the database, the problem is that in many cases prepare statement return false. I solved the issue by making new connection every while, but this caused another issue in other functions "2006: Mysql server has gone away" See code below please:
Here and in other functions the prepare return false.
function isPostingAllowed() {
$this->setPsSelectUsers($this->_connection->prepare("Select * from userpermissions where userpermissions.UserId = ? and userpermissions.PermissionId = 1"));
$this->_psSelectUsers->bind_param('i',$this->UserId);
$this->_psSelectUsers->execute();
if ( false===$this->_psSelectUsers ) {
die('prepare() failed: ' . htmlspecialchars($this->_connection->error));}
if ($this->_psSelectUsers->fetch()){
return true;
}else{
return false;
}}
function setPsSelectUsers($stmt) {
$this->unsetPsSelectUsers();
// mysqli_close($this->_Connection);
// $this-> __construct();
$this->_psSelectUsers= $stmt;}
When I uncomment these two lines The first function will work and prepare staement will not return false, but in this case the following function will throw error 2006:
function checkUserAuthentication() {
$this->setPsSelectUsers($this->_connection->prepare("SELECT UserLogInName FROM Users WHERE UserLogInName=? AND UserPassword=?"));
$this->_psSelectUsers->bind_param('ss',$this->UserLogInName, $this->UserPassword);
$this->_psSelectUsers->execute();
if ($this->_psSelectUsers->fetch()){
return true;
}else{
return false;
}}
So How to solve the first problem without making new problem?
Problem:
... I use mysqli prepare statement to insert or select data from the database, the problem is that in many cases prepare statement return false.
That's because you're running the queries in out of order fashion i.e. you're executing ->prepare() before closing the previous statement object. Given your current code, add the following error reporting code in your prepared statements,
if(!$this->_connection->prepare(...)){
printf('errno: %d, error: %s', $this->_connection->errno, $this->_connection->error);
die();
}
If you look at $this->_connection->error, you'll see the following error,
Commands out of sync; you can't run this command now
This issue has been documented in many forums, such as:
From the MySQL documentation,
If you get Commands out of sync; you can't run this command now in your client code, you are calling client functions in the wrong order.
From this SO thread,
You can't have two simultaneous queries because mysqli uses unbuffered queries by default (for prepared statements;...
Solution:
Execute the commands in correct order, (See this example code)
Open up the connection (Once: at the very beginning, not during the execution of every query)
Create a prepared statement
Bind parameters
Execute the query
Bind result variables
Fetch value into those bound variables
Close the statement object
Close the connection (Once: at the very end, not during the execution of every query)
(Follow steps 2 to 7 for executing all of your queries, though one or more steps might be optional based on of your query)
So the solution is, close the previous statement object before calling ->prepare() again. Take this method call $this->unsetPsSelectUsers(); out of the setPsSelectUsers() method and place it before the if ($this->_psSelectUsers->fetch()){...}else{...} block of isPostingAllowed() and checkUserAuthentication() methods. Furthermore, save the status of $this->_psSelectUsers->fetch() method call in a variable and use it in the subsequent if block. So your code should be like this:
public function isPostingAllowed() {
$this->setPsSelectUsers($this->_connection->prepare("Select * from userpermissions where userpermissions.UserId = ? and userpermissions.PermissionId = 1"));
if(!$this->_psSelectUsers){
printf('errno: %d, error: %s', $this->_connection->errno, $this->_connection->error);
die();
}
$this->_psSelectUsers->bind_param('i',$this->UserId);
$this->_psSelectUsers->execute();
$status = $this->_psSelectUsers->fetch();
$this->unsetPsSelectUsers();
if ($status){
return true;
}else{
return false;
}
}
private function setPsSelectUsers($stmt){
$this->_psSelectUsers= $stmt;
}
private function unsetPsSelectUsers() {
if (isset($this->_psSelectUsers)) {
$this->_psSelectUsers->close();
unset($this->_psSelectUsers);
}
}
public function checkUserAuthentication() {
$this->setPsSelectUsers($this->_connection->prepare("SELECT UserLogInName FROM Users WHERE UserLogInName=? AND UserPassword=?"));
if(!$this->_psSelectUsers){
printf('errno: %d, error: %s', $this->_connection->errno, $this->_connection->error);
die();
}
$this->_psSelectUsers->bind_param('ss',$this->UserLogInName, $this->UserPassword);
$this->_psSelectUsers->execute();
$status = $this->_psSelectUsers->fetch();
$this->unsetPsSelectUsers();
if ($status){
return true;
}else{
return false;
}
}
Moreover, you don't have to use this setPsSelectUsers() method anymore, you can directly use the property $_psSelectUsers in your methods like this:
$this->_psSelectUsers = $this->_connection->prepare(...);

Extend PDO Execute() to Count Queries on Page

Ok, similar questions has been asked and answered before. However, I want to count prepared queries as well. I thought I could just use the prepare() function to increment the query count but that provided me some questionable results. Right now, it's telling me 10 queries are being executed... Generally I only use prepared statements unless the data is static or cannot be modified in anyway.
I found this code from another question that was identical to this. Except that the code only was counting queries that were executed using the query() and exec() functions. I tried to modify it but as I said before it isn't counting any prepared queries but the queries are still executed and return results...
class PDOEx extends PDO
{
private $queryCount = 0;
public function query($query)
{
++$this->queryCount;
return parent::query($query);
}
public function exec($statement)
{
++$this->queryCount;
return parent::exec($statement);
}
public function execute($args = null)
{
++$this->queryCount;
if (!is_array($args)) {
$args = func_get_args();
}
return parent::execute($args);
}
public function GetCount()
{
return $this->queryCount;
}
}
I just want to say thanks in advance. Please, if you can point me in the right direction it would be much appreciated, thanks!
The reason prepared queries aren't being counted is because it returns a PDOStatement, which is different than PDO. You could just ask the database how many queries were run on a given session, so as long as you don't keep create new \PDO objects then the result will be correct. Keep in mind even that query is counted as a query, so if you want to exclude it subtract 1 from the value.
Code
$db = new \PDO('mysql:host=127.0.0.1;dbname=sandbox', 'localuser', 'password');
$db->query('SELECT * FROM user WHERE id = 5');
$db->query('UPDATE user SET name = 'Demo' WHERE id = 5');
$db->query('DELETE FROM user WHERE id = 5');
echo $db->query('SHOW SESSION STATUS LIKE "Questions"')->fetchColumn(1);
Output
4

convert mysql to pdo

So i have a function thats supposed to handle all data execute operations: sql
function loadResult($sql)
{
$this->connect();
$sth = mysql_query($sql);
$rows = array();
while($r = mysql_fetch_object($sth)) {$rows[] = $r;}
$this->disconnect();
return $rows;
}
I want to convert it to pdo and this is what i have so far: pdo
function loadResult($sql)
{
$this->connect();
$sth = $this->con->prepare($sql);
//execute bind values here
$sth->execute();
$rows = array();
while ( $r = $sth->fetch(PDO::FETCH_OBJ) ) {$rows[] = $r;}
$this->disconnect();
return $rows;
}
Here is an example of a function on how am using it to view data from the database:
function viewtodolist()
{
$db=$this->getDbo(); //connect to database
$sql="SELECT * FROM mcms_todolist_tasks";
//maybe the bind values are pushed into an array and sent to the function below together with the sql statement
$rows=$db->loadResult($sql);
foreach($rows as $row){echo $row->title; //echo some data here }
}
I have just pulled out the important snippets so some variables and methods are from other php classes. Somehow, the mysql query works fine, but the PDO query is giving me headaches on how to include bindValue paremeters most probably in the viewtodolist() function to make it reusable. Any suggestions/recommendations are welcome.
Since your existing function accepts a fully-formed SQL string, with no placeholders, you don't need to use prepare + bind. Your code as written should work fine, or you could use PDO::query() to execute the SQL in one step.
If you want to use parameterised queries, then your loadResult function is going to have to change a bit, as is the way you write your SQL. The example SQL you give doesn't actually have anything in that could be turned into a parameter (column names and table names can't be parameters as discussed here), but I'll use an imaginary variation:
// Get the todo tasks for a particular user; the actual user ID is a parameter of the SQL
$sql = "SELECT * FROM mcms_todolist_tasks WHERE user_id = :current_user_id";
// Execute that SQL, with the :current_user_id parameter pulled from user input
$rows = $db->loadResult($sql, array(':current_user_id' => $_GET['user']));
This is a nice secure way of putting the user input into the query, as MySQL knows which parts are parameters and which are part of the SQL itself, and the SQL part has no variables that anyone can interfere with.
The simplest way of making this work with your existing loadResult function would be something like this:
// Function now takes an optional second argument
// if not passed, it will default to an empty array, so existing code won't cause errors
function loadResult($sql, $params=array())
{
$this->connect();
$sth = $this->con->prepare($sql);
// pass the parameters straight to the execute call
$sth->execute($params);
// rest of function remains the same...
There are cleverer things you can do with parameterised queries - e.g. binding variables to output parameters, preparing a query once and executing it multiple times with different parameters - but those will require more changes to the way your calling code works.

Doctrine andWhere orWhere doesn't seem to bind more than one parameter

I have a query like this:
$name = "field1";
$name2 = "field2";
$value = "searchTerm";
$query->select('*')->
from("TableName")->
where($name . " = ?", array($value))->
andWhere($name2 . " = ?", array($value));
I was suprised to see that when this executes the query generates MS SQL Error 102 (syntax error) because the query sent to sql server looks like this:
SELECT * FROM TableName WHERE field1 = 'searchTerm' AND field2 = ?
The question mark was taken literally in each additional condition added to the query! :o
Perhaps I am doing something wrong and someone can set me straight here.
Whew! I found the answer!
For whatever reason doctrine does not use PDO's prepared statement functionality for SQL server. Connection/Mssql.php substitutes any parameters within the query string and passes an empty array to Connection::execute. This would be fine:
If the prepared statements didn't work (maybe they didn't work in sql 2000 when the connection driver was first written?) :/
If the substitution actually successfully substituted more than one value! :o
The fix is easy:
in Connection/Mssql.php on line 314 change the execute function below:
public function execute($query, array $params = array())
{
if(! empty($params)) {
$query = $this->replaceBoundParamsWithInlineValuesInQuery($query, $params);
}
return parent::execute($query, array());
}
to:
public function execute($query, array $params = array())
{
return parent::execute($query, $params);
}
You could just as easily remove the overridden function.
Don't forget to make the same changes to exec which appears just below the execute function in the same code file.
Also, you may wish to test this before using it with versions of SQL server earlier than 2005 as I have not tested this solution with that version.

Categories