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.
Related
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.
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 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
I have a complex query that gets executed like this:
if ($stmt = $dbi->prepare($pt_query)) {
$stmt->bind_param('ssssssssi', $snome,$scognome,$ssocieta,$svia,$slocalita,$sprovincia,$scap,$stelefono,$sfax,$uid);
$stmt->execute();
echo $dbi->error;
$stmt->close();
} else {
printf("Error -> %s\n", $dbi->error);
}
This thing is failing without any error, it simply doesn't update the database. Since there is a ton of data that gets treated before this thing I would like to know if there is any way to show the actual query that mysqli is executing in order to understand where the problem is.
Thank you.
If your statement is failing, you should check $stmt->error (as opposed to $dbi->error). As far as getting the actual text of the query: it's not possible. When using prepared statements, the library is using a special protocol that doesn't generate an actual query string for each ->execute() call.
You could turn on logging on the MySQL DB itself, ie. add a log=logfile line to my.ini.
Refer to the MySQL documentation for more information if needed.
Based on the PHP mysql website there is no actual way of doing it. But you may try this function as it gives you errors in your query.
Here's a tool I found that may help MySQLi Prepare Statement checker
This question already has answers here:
Getting raw SQL query string from PDO prepared statements
(16 answers)
Closed 6 years ago.
In PHP, when accessing MySQL database with PDO with parametrized query, how can you check the final query (after having replaced all tokens)?
Is there a way to check what gets really executed by the database?
So I think I'll finally answer my own question in order to have a full solution for the record. But have to thank Ben James and Kailash Badu which provided the clues for this.
Short Answer
As mentioned by Ben James: NO.
The full SQL query does not exist on the PHP side, because the query-with-tokens and the parameters are sent separately to the database.
Only on the database side the full query exists.
Even trying to create a function to replace tokens on the PHP side would not guarantee the replacement process is the same as the SQL one (tricky stuff like token-type, bindValue vs bindParam, ...)
Workaround
This is where I elaborate on Kailash Badu's answer.
By logging all SQL queries, we can see what is really run on the server.
With mySQL, this can be done by updating the my.cnf (or my.ini in my case with Wamp server), and adding a line like:
log=[REPLACE_BY_PATH]/[REPLACE_BY_FILE_NAME]
Just do not run this in production!!!
You might be able to use PDOStatement->debugDumpParams. See the PHP documentation .
Using prepared statements with parametrised values is not simply another way to dynamically create a string of SQL. You create a prepared statement at the database, and then send the parameter values alone.
So what is probably sent to the database will be a PREPARE ..., then SET ... and finally EXECUTE ....
You won't be able to get some SQL string like SELECT * FROM ..., even if it would produce equivalent results, because no such query was ever actually sent to the database.
I check Query Log to see the exact query that was executed as prepared statement.
I initially avoided turning on logging to monitor PDO because I thought that it would be a hassle but it is not hard at all. You don't need to reboot MySQL (after 5.1.9):
Execute this SQL in phpMyAdmin or any other environment where you may have high db privileges:
SET GLOBAL general_log = 'ON';
In a terminal, tail your log file. Mine was here:
>sudo tail -f /usr/local/mysql/data/myMacComputerName.log
You can search for your mysql files with this terminal command:
>ps auxww|grep [m]ysqld
I found that PDO escapes everything, so you can't write
$dynamicField = 'userName';
$sql = "SELECT * FROM `example` WHERE `:field` = :value";
$this->statement = $this->db->prepare($sql);
$this->statement->bindValue(':field', $dynamicField);
$this->statement->bindValue(':value', 'mick');
$this->statement->execute();
Because it creates:
SELECT * FROM `example` WHERE `'userName'` = 'mick' ;
Which did not create an error, just an empty result. Instead I needed to use
$sql = "SELECT * FROM `example` WHERE `$dynamicField` = :value";
to get
SELECT * FROM `example` WHERE `userName` = 'mick' ;
When you are done execute:
SET GLOBAL general_log = 'OFF';
or else your logs will get huge.
What I did to print that actual query is a bit complicated but it works :)
In method that assigns variables to my statement I have another variable that looks a bit like this:
$this->fullStmt = str_replace($column, '\'' . str_replace('\'', '\\\'', $param) . '\'', $this->fullStmt);
Where:
$column is my token
$param is the actual value being assigned to token
$this->fullStmt is my print only statement with replaced tokens
What it does is a simply replace tokens with values when the real PDO assignment happens.
I hope I did not confuse you and at least pointed you in right direction.
The easiest way it can be done is by reading mysql execution log file and you can do that in runtime.
There is a nice explanation here:
How to show the last queries executed on MySQL?
I don't believe you can, though I hope that someone will prove me wrong.
I know you can print the query and its toString method will show you the sql without the replacements. That can be handy if you're building complex query strings, but it doesn't give you the full query with values.
I think easiest way to see final query text when you use pdo is to make special error and look error message. I don't know how to do that, but when i make sql error in yii framework that use pdo i could see query text