162

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?

JB Hurteaux
  • 4,428
  • 6
  • 32
  • 35
  • 6
    I really wish there was a way to do that. It's been bothering me for a while. – Nathan H Nov 24 '09 at 01:38
  • 2
    For PHP >= 5.1, take a look at http://www.php.net/manual/en/pdostatement.debugdumpparams.php – Mawg says reinstate Monica Jun 03 '14 at 08: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) – a coder Nov 14 '14 at 13:50
  • 2
    Related: [Getting raw SQL query string from PDO prepared statements](http://stackoverflow.com/q/210564/562769) – Martin Thoma Mar 27 '15 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. – RN Kushwaha Apr 11 '19 at 12:07

9 Answers9

63

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

JB Hurteaux
  • 4,428
  • 6
  • 32
  • 35
  • 1
    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. – a coder Nov 14 '14 at 13:48
  • 2
    Since PHP ver . `7.2` it can be achived with `$stmt->debugDumpParams();` – biesior Jul 26 '20 at 14:02
44

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

Michael
  • 467
  • 3
  • 2
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.

Ben James
  • 121,135
  • 26
  • 193
  • 155
11

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

Kailash Badu
  • 406
  • 2
  • 5
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.

Chris Adams
  • 651
  • 6
  • 8
  • 5
    So, you made your query open to SQL injection, congrats :) – Your Common Sense Apr 12 '13 at 06:57
  • 5
    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. – lsl Jun 13 '13 at 04:04
  • 1
    SET GLOBAL log_output = 'TABLE'; SELECT * FROM mysql.general_log; – Richard Tyler Miles Oct 21 '18 at 02:27
  • 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! – Artfaith Jun 11 '20 at 13:12
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.

6bytes
  • 5,858
  • 9
  • 37
  • 42
  • 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...) – JB Hurteaux Nov 24 '09 at 13:51
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?

Community
  • 1
  • 1
Cristiano
  • 1,091
  • 7
  • 3
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.

Scott Saunders
  • 29,840
  • 14
  • 57
  • 64
-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