We are using PHP >= 7.2 with PDO with the MySQL driver to execute multiple SQL statements in one exec() call. We use this concept to create DB Migration scripts, which are executed by PHP. These plain SQL scripts contain multiple SQL statements, which are executed by our PHP framework, like this:
$conn = PDO::conn('mysql:......','user','pw');
$ret = $conn->exec('
DROP VIEW IF EXISTS v_my_view;
CREATE VIEW v_my_view AS SELECT id,name FROM mytable;
');
// $ret now contains 1
if ($ret === FALSE) { // some error handling }
This works fine, even for many statements within one exec() call: The database executes all statements.
Now if the SQL itself contains an error, (e.g. syntax error, or unknown column etc.), the call to exec() returns exactly the same as before (1), and also DBH::errorInfo() returns no error:
$conn = PDO::conn('mysql:......','user','pw');
$ret = $conn->exec('
DROP VIEW IF EXISTS v_my_view;
-- The following statement creates an SQL error (unknown column):
CREATE VIEW v_my_view AS SELECT id,name_not_known FROM mytable;
');
// $ret still contains 1
var_dump($conn->errorInfo()); // Returns Error Code 0, all fine.
So it seems that PDO::exec() does support multiple statements, but does not implement any error handling / abort mechanism.
This also seems to be a MySQL-only problem: The same mechanism works fine on a PostgreSQL database.
Is there any way we can force PDO to "stop on errors" on multiple statement queries?
Yes, there is. It is explained in my PDO tutorial, under the running multiple queries section.
Basically you need to temporarily switch the emulation mode on with
$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, true);
and then run your SQL dump using a regular query() method. and then you will have to loop over results of every query to catch the error. As your queries don't seem to return any data, the loop could be as simple as
do {} while ($stmt->nextRowset());
Note that you must enable exception mode for PDO in order to get an error exception for the erroneous query. In case you will need some data from the queries, such as insert id, you may add necessary commands inside the curly braces.
Related
I new there are lots of answer as well as accepted answers related to this question but none of them solve my problem. Still I am getting this error.
Procedures:
CREATE PROCEDURE getAllProducts()
BEGIN
SELECT * FROM products;
END //
CREATE PROCEDURE getAllCategories()
BEGIN
SELECT * FROM category;
END //
Connection & calling:
$link = mysql_connect($host,$username,$password) or die(mysql_error());
mysql_select_db($database, $link) or die(mysql_error());
$allProducts = mysql_query("CALL getAllProducts");
while($row = mysql_fetch_array($allProducts)) { }
$allCategory = mysql_query("CALL getAllCategories");
while($row = mysql_fetch_array($allCategory)) { }
I've even called mysql_free_result($allProducts) before executing the next query. But nothing happens.
mysql_get_client_info() return mysqlnd 5.0.5-dev - 081106 - $Revision: 1.3.2.27 $
I found that the problem only arises if I run two queries.
As the MySQL-Documentation for 'Commands out of sync' points out:
[...] It can also happen if you try to execute two queries that return data
without calling mysql_use_result() or mysql_store_result() in between.
The Documentation for mysql_use_result() says e.g.:
After invoking mysql_query() or mysql_real_query(), you must call
mysql_store_result() or mysql_use_result() for every statement that
successfully produces a result set (SELECT, SHOW, DESCRIBE, EXPLAIN,
CHECK TABLE, and so forth). You must also call mysql_free_result()
after you are done with the result set.
Basically you need to tell your client what it should do with the result.
Well, usually this error occurs because there are still results pending from the query. There are mysqli_store_result and mysqli_free_result functions available. Since you are using mysql and mysql extension does not have such functions, you can try closing the connection after executing the first procedure and establishing the connection again to execute next procedure. Though this is not the perfect solution, but it will work in your case.
mysql_close($connection);
$connection = mysql_connect("localhost","username","password");
You can also try
mysql_free_result($allProducts);
Stored procedures always return an extra result set with errors/warnings information. As such, your stored procedures return multiple result sets (the actual result set from your select query and the extra errors/warnings result set). Calling mysql_fetch_array in a loop you only saturate one of them, leaving the other still pending, causing the error you see.
I don't know how to fix it with vanilla mysql_ library, with mysqli_ you can issue mysqli_multi_query, and then only use the first result set. See the example in the docs.
It's not a fix per se, but if you insist on staying with mysql_* functions, and assuming that you actually want to work with more complicated stored procedures (i.e. that the ones you supplied are just a simplified example) - you can change the stored procedures code to write the resultset into a temporary table (e.g. tmp_getAllProducts) instead of returning it, and then SELECT from it in your PHP.
This is what worked for me a while back when I was stuck with mysql_* and couldn't upgrade...
This is a known limitation of the mysql extension. You must either not use more than one stored procedure per connection or upgrade to mysqli.
https://bugs.php.net/bug.php?id=39727
I am trying to extract two fields from tables accesscode and student the update the table borrowers with the data that i have extracted from the previous tables.
$q=$db->query("SELECT regnum, accesscode FROM student,student_accesscode WHERE student.id=student_accesscode.studentid");
while($qd=$q->fetch(PDO::FETCH_ASSOC))
{
$access=$qd['accesscode'];
$regnum=$qd['regnum'];
$q2=$db->exec("UPDATE borrowers SET cardnumber='$access' WHERE cardnumber='$regnum'");
if($q2)
{
echo $access.' '. $regnum.'<br/>';
}
else
{
echo'erro....<br/>';
}
}
?>
Appending $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING); before $db->query() probably gives you an Exception...
Prior to the $db->setAttribute, you will get this:
PDO::prepare(): 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.
So, instead of fetch(), use fetchAll() with foreach loop, it will make you less insane.
Ref. from php.net
For a statement that you need to issue multiple times, prepare a PDOStatement
object with PDO::prepare() and issue the statement with PDOStatement::execute().
From the PHP exec page at http://www.php.net/manual/en/pdo.exec.php
I don't know if this is the problem, I always use prepare and execute. It could just be for performance reasons. Something to try anyway.
I need to execute two SQL statements together because connection_id() in the first statement will be used in the Mysql view wp_statistics_benchmarks.
Without the connection_id(), the wp_statistics_benchmarks is an empty view. The following SQL works fine and get results:
replace into wp_params (`view_name` , `param1_val`, `connection_id`)
values ('benchmarks', 484 , connection_id())
;
select * from wp_statistic_benchmarks;
But, to work with wordpress, the following code doesn't work:
$mysqli = new mysqli(.....);
$results = $this->_wpdb->query("
replace into wp_params (`view_name`, `param1_val`, `connection_id`)
values ('benchmarks', $connected_from, $mysqli->thread_id);
select * FROM `wp_statistic_benchmarks`;"
);
How can I convert these two mysql codes into Wordpress wpdb queries?
Use the wpdb object twice.
$this->_wpdb->query('replace into ...');
$rows = $this->_wpdb->get_results('select ...')
Let me put it another way, select * from wp_stat ... and replace into wp_params ... from your original "mysql codes" are separate statements without any relation to each other.
You think that you need to run them in sequence, whereas in fact you can have a cup of coffee or even travel around the earth in between those replace into and select statements and they would still do the same thing. If that is not the case, then your question lacks information necessary to provide a good answer because wp_params is not a standard table in wordpress and neither is the view. I don't think you understand your problem.
Besides, running them as I suggest is equivalent with your "mysql codes". Moreover, $wpdb->query returns the number of affected rows or false, so you will never be able to run a select statement with $wpdb->query() to retrieve a set of tuples.
How can I convert these two mysql codes into Wordpress wpdb queries?
You can't. That's because you're using wpdb and it only supports one query per ->query() call. However, if you're using Mysqli with wpdb, you can use the multi_query() method of it with wpdb. Here is how:
To use multiple queries, you need to ensure that wpdb uses Mysqli (e.g. define the USE_EXT_MYSQL constant as FALSE in your Wordpress config).
Then you can obtain the mysqli instance from the wpdb object, either with reflection or a helper class/module:
abstract class wpdb_dbh extends wpdb
{
static function from(wpdb $wpdb) {
return $wpdb->dbh;
}
}
Mysqli is then available without creating a new instance:
$mysqli = wpdb_dbh::from($this->_wpdb);
As this is a valid Mysqli instance you can run multi query.
But just obtaining the same Mysqli instance as wpdb uses it probably the most important thing here as otherwise your open an additional connection with new mysqli(...) which you need to prevent.
Additionally take care that $mysqli->thread_id is a fitting replacement to connection_id() following the same formatting/encoding. You should be able to use connection_id() directly anyway, so I actually see not much reason to access the thread_id member, but it's perhaps only because you tried some alternatives and I'm just over-cautious.
The ';' query delimiter is purely an SQL shell convenience and is not a part of the MySQL dialect so you're correct that your code doesn't work.
Here's the actual replacement code:
$mysqli = new mysqli(.....);
$this->_wpdb->query(
"replace into wp_params
(`view_name`, `param1_val`, `connection_id`)
values ('benchmarks', $connected_from, $mysqli->thread_id)");
$results = $this->_wpdb->query("select * FROM `wp_statistic_benchmarks`");
This is the same as Ярослав's answer above.
Update:
If your code is still not working you might have to enable persistent connections in Wordpress.
Update 2:
There was a missing space between in the second query's select statement and the * shorthand all columns selector. Interestingly this may or may not cause an issue for you, it doesn't seem to bother my MySQL 5.5 command line shell.
If I understand your requirements (and I do not know wordpress), you are inserting a row to wp_params with a column called connection_id. I would assume that this value will be unique on the table. I would be tempted to add an integer autoincrement id field to the table and then get the value of that (last insert id). Then use this id in a WHERE clause when selecting from the view.
I wrote a database migration PHP script, that loops a list of SQL files and executes their content. It should help to automate the project setupd and updates. Now I'm getting errors like this:
mapCoursesToSport.sql: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'DELIMITER |
DROP PROCEDURE IF EXISTS mapCoursesToSport|
CREATE PROCEDURE mapCo' at line 1
I was getting the same error when I was passing to the script a file with a view definition, that also was using DELIMITERs. Then I found the workaround just to remove the delimiters from the SQL file. And it worked. But now it's not an option, since stored procedures really need delimiter definitions.
So, how can I define MySQL stored procedures from PHP? (Or maybe morre generally: How should this SDELIMITER be handeled?)
You can use native PHP function mysqli::query and mysqli::prepare alternatively to create stored procedure:
http://php.net/manual/en/mysqli.quickstart.stored-procedures.php
Using mysqli::multi_query also works but is a bit more tricky to handle since you might need to count the number of query to execute upfront before executing them one by one (provided the final query delimiter is optional, counting the queries can be tedious)
http://php.net/manual/en/mysqli.multi-query.php
<?php
// Using mysqli extension below in object-oriented mode
// after having executing queries
// with mysqli::multi_query
do {
$queryResult = $mysqli->use_result();
unset($results);
while ($result = $queryResult->fetch_array(MYSQLI_ASSOC)) {
$results[] = $result;
}
$queryResult->close();
if ($mysqli->more_results()) {
$mysqli->next_result();
}
$queryCount--;
} while ($queryCount > 0);
P.S.: The reason I'm not using mysqli:more_results nor mysqli:next_result as loop statement is that some query might be properly executed without returning any result. In such case, we don't want to break the loop before all queries have been executed.
I am trying to log the sql queries when a script is running. I am using zend framework and I already checked zend db profiler and this is not useful as this shows "?" for the values in a insert query..I need the actual values itself so that I can log it in a file. I use getAdapter()->update method for the update queries so I don' know if there is a way to get queries and log it. Please let me know if there is a way to log the queries.
regards
From http://framework.zend.com/manual/en/zend.db.profiler.html
The return value of getLastQueryProfile() and the individual elements of getQueryProfiles() are Zend_Db_Profiler_Query objects, which provide the ability to inspect the individual queries themselves:
getQuery() returns the SQL text of the query. The SQL text of a prepared statement with parameters is the text at the time the query was prepared, so it contains parameter placeholders, not the values used when the statement is executed.
getQueryParams() returns an array of parameter values used when executing a prepared query. This includes both bound parameters and arguments to the statement's execute() method. The keys of the array are the positional (1-based) or named (string) parameter indices.
When you use Zend_Db_Profiler_Firebug it will also show you the queries on the returned pages in the Firebug console along with any bound parameters.
I know you have got your answer though just for reference...
I have traversed hundred of pages, googled a lot but i have not found any exact solution.
Finally this worked for me. Irrespective where you are in either controller or model. This code worked for me every where. Just use this
//Before executing your query
$db = Zend_Db_Table_Abstract::getDefaultAdapter();
$db->getProfiler()->setEnabled(true);
$profiler = $db->getProfiler();
// Execute your any of database query here like select, update, insert
//The code below must be after query execution
$query = $profiler->getLastQueryProfile();
$params = $query->getQueryParams();
$querystr = $query->getQuery();
foreach ($params as $par) {
$querystr = preg_replace('/\\?/', "'" . $par . "'", $querystr, 1);
}
echo $querystr;
Finally this thing worked for me.
There are a few logs MySQL keeps itself.
Most notably:
The binary log (all queries)
Slow query log (queries that take longer than x time to execute)
See: http://dev.mysql.com/doc/refman/5.0/en/server-logs.html