170

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?

5
  • 6
    I really wish there was a way to do that. It's been bothering me for a while. Commented Nov 24, 2009 at 1:38
  • 2
    For PHP >= 5.1, take a look at php.net/manual/en/pdostatement.debugdumpparams.php Commented Jun 3, 2014 at 8:59
  • 1
    There is a bug in debugDumpParams() - no values are included in ouput. The only way to (easily) check queries as executed by mysql is to temporarily enable the log in my.cnf (h/t to @JB Hurteaux) Commented Nov 14, 2014 at 13:50
  • 2
    Related: Getting raw SQL query string from PDO prepared statements Commented Mar 27, 2015 at 12:04
  • The easy and best way is to use SET global general_log = 1; and SET global log_output = 'table'; then simply query from the mysql database as SELECT * FROM mysql.general_log. It shows the prepare statement and and the actual query executed on the database. Commented Apr 11, 2019 at 12:07

9 Answers 9

64

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!!!

Sign up to request clarification or add additional context in comments.

2 Comments

Be sure to restart mysql for changes in my.cnf to take effect. After running your query be sure to comment out the log line, and restart mysql again to stop logging.
Since PHP ver . 7.2 it can be achived with $stmt->debugDumpParams();
48

You might be able to use PDOStatement->debugDumpParams. See the PHP documentation .

2 Comments

Good answer, but to display the param values you might need a patch.
+1 This should probably have been accepted as the answer. It allows mixing of SQL with other debug traces from PHP, rather than having to look in several places.
30

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.

3 Comments

Accepting this for now as it makes sense. It's not very convenient for debugging then...
Is there a way maybe we can see the "stream" of sql statements that actually gets executed? Maybe it can still be of some help, better than nothing.
$stmt->debugDumpParams(); should come close.
12

I check Query Log to see the exact query that was executed as prepared statement.

Comments

5

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.

4 Comments

So, you made your query open to SQL injection, congrats :)
Actually no, firstly, pdo doesn't have any way to set dynamic columns in this fashion. This is the best way to do it. Secondly, dynamically setting fields in this way: dynamicField='userName' doesn't leave any room for injection. As long as you validate your column names against a whitelist its impossible to inject this method of building sql.
SET GLOBAL log_output = 'TABLE'; SELECT * FROM mysql.general_log;
What the? Don't use this!!! This simply opens a hole in your SQL queries for an SQL injections and removes any sense of PDO itself!
1

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.

1 Comment

Yes, thanks. I also though of a custom function to do a manual replace of tokens after having seen the answer from Ben James. Not very convenient when there are several tokens but there might be a way to automate this (if we can access all the bindings created) and create a generic function. On the other hand, what I want is really the Query as generated before execution. Creating a function of my own to do that does not guarantee I'll get the same result (ex: taking into account inserted quotes around String parameters...)
1

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?

Comments

0

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.

Comments

-1

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.