PHP DB2 Get Full Query after db2_execute() - php

We are using db2_prepare and db2_execute to prepare queries in a generic function. I am trying to have a debug method to get the full prepared query after the '?' values have been replaced by the db2_execute function.
Is there an efficient way of doing this besides manually replacing each '?' with the parameters I am passing in? i.e. is there a flag that can be set for db2_execute?
Example:
$params = array('xyz','123');
$query = "SELECT * FROM foo WHERE bar = ? AND baz = ?";
$sqlprepared = db2_prepare($CONNECTION, $query);
$sqlresults = db2_execute($sqlprepared,$params);
I would like the $sqlresults to contain the full prepared query:
"SELECT * FROM foo WHERE bar = 'xyz' AND baz = '123'";
I have looked through the docs and do not see any obvious way to accomplish this, but I imagine there must be a way.
Thank you!

db2_execute() does not replace parameter markers with values. Parameters are sent to the server and bound to the prepared statement there.
The CLI trace, which can be enabled on the client as explained here, will contain the actual parameter values. Keep in mind that the trace seriously affects application performance.

I ended up writing a loop to replace the '?' parameters with a simple preg_replace after and outputting the query in my 'debug' array key:
$debugquery = $query;
foreach($params as $param) {
$debugquery = preg_replace('/\?/',"'".$param."'",$debugquery,1);
}
return $debugquery;
This handled what I needed to do (to print the finalized query for debugging purposes). This should not be run in Production due to performance impacts but is useful to look at the actual query the server is trying to perform (if you are getting unexpected results).

Related

PHP PDO mysql prepared statment and join

I have a question.
I have the following query:
$query = "select * from module,bloc where module.id_bloc = ?";
I tried to bind the value so I did:
$stmt = $this->db->prepare($query);
$stmt->bindValue(1, "bloc.id_bloc");
But, when I test I don't get any result on my browser.
It's weird because when I replace directly inside like the following code:
$query = "select * from module,bloc where module.id_bloc = bloc.id_bloc";
I get the the right result on my browser.
Could someone explain to me why it doesn't work when I am doing a bindValue?
It will not work because, when bound, a string will be quoted. (Or, for all intents and purposes, work as if it were quoted, however PDO may handle it behind the scenes.) Then, your query is interpreted as:
select * from module,bloc where module.id_bloc = 'bloc.id_bloc'
That is: It will be interpreted as a literal string, rather than a reference to a table column, and will obviously not give you the expected result. There is no need for binding it to begin with.
If, for some reason, you need to run a query with a variable table/column name from an unsafe source, you will have to manually format/sanitize it; see here for an example of how to do it.

shorthand PDO query

Currently to perform a query with PDO, I use the following lines of code:
$sql = "SELECT * FROM myTable WHERE id = :id";
$stmt = $conn->prepare($sql);
$stmt->bindParam(':id', $id);
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
And after some research, I found a shorter way of executing the same command:
$stmt_test = $conn->prepare("SELECT * FROM status WHERE status_id = ?");
$stmt_test->execute([$id])->fetchAll(PDO::FETCH_ASSOC);
$result = $stmt_test->fetchAll(PDO::FETCH_ASSOC);
From there I thought I could possibly make it even shorter with the following code:
$stmt_test = $conn->prepare("SELECT * FROM status WHERE status_id = ?");
$result = $stmt_test->execute([$id])->fetchAll(PDO::FETCH_ASSOC);
But I get the following error:
Fatal error: Call to a member function fetchAll() on a non-object in
/home/.../index.php on line 20
QUESTION: Why am I getting this error? From my understanding, $stmt_test->execute([$id]) should be executing first, then the result of that would execute the ->fetchAll(PDO::FETCH_ASSOC) and from there return the array to $result, but since the error is happening, something must be flawed in my logic. What am I doing wrong? Also, does anyone know a better shorthand method to perform the previous query?
So you've got an answer for the question "Why I am getting this error", but didn't get one for the "shorthand PDO query".
For this we will need a bit of a thing called "programming".
One interesting thing about programming is that we aren't limited to the existing tools, like with other professions. With programming we can always create a tool of our own, and then start using it instead of a whole set of old tools.
And Object Oriented Programming is especially good at it, as we can take an existing object and just add some functionality, leaving the rest as is.
For example, imagine we want a shorthand way to run a prepared query in PDO. All we need is to extend the PDO object with a new shorthand method. The hardest part is to give the new method a name.
The rest is simple: you need only few lines of code
class MyPDO extends PDO
{
public function run($sql, $bind = NULL)
{
$stmt = $this->prepare($sql);
$stmt->execute($bind);
return $stmt;
}
}
This is all the code you need. You may store it in the same file where you store your database credentials. Note that this addition won't affect your existing code in any way - it remains exactly the same and you may continue using all the existing PDO functionality as usual.
Now you have to change only 2 letters in PDO constructor, calling it as
$conn = new MyPDO(...the rest is exactly the same...);
And immediately you may start using your shiny new tool:
$sql = "SELECT * FROM myTable WHERE id = :id";
$result = $conn->run($sql, ['id' => $id])->fetchAll(PDO::FETCH_ASSOC);
Or, giving it a bit of optimization,
$result = $conn->run("SELECT * FROM myTable WHERE id = ?", [$id])->fetchAll();
as you can always set default fetch mode once for all, and for just a single variable there is no use for the named placeholder. Which makes this code a real shorthand compared to the accepted answer,
$stmt_test = $conn->prepare("SELECT * FROM status WHERE status_id = ?");
$stmt_test->execute([$id]);
$result = $stmt_test->fetchAll(PDO::FETCH_ASSOC);
and even to the best answer you've got so far,
$result = $conn->prepare("SELECT * FROM status WHERE status_id = ?");
$result->execute([$id]);
not to mention that the latter is not always usable, as it fits for getting an array only. While with a real shorthand any result format is possible:
$result = $conn->run($sql, [$id])->fetchAll(); // array
$result = $conn->run($sql, [$id])->fetch(); // single row
$result = $conn->run($sql, [$id])->fetchColumn(); // single value
$result = $conn->run($sql, [$id])->fetchAll(PDO::FETCH_*); // dozens of different formats
$stmt_test->execute([$id]) returns a boolean value. That mean that
$result = $stmt_test->execute([$id])->fetchAll(PDO::FETCH_ASSOC);
isn't valid. Instead you should do
$stmt_test->execute([$id]);
$result = $stmt_test->fetchAll(PDO::FETCH_ASSOC);
The error you're getting comes form the way PDO was designed. PDOStatement::execute() doesn't return the statement, but a boolean indicating success. The shortcut you want therefore isn't possible.
See function definition in http://php.net/manual/en/pdostatement.execute.php
Additionally let me add that forEach() often (not always) is a code smell and takes relatively much memory as it has to store all rows as PHP values.
I believe PDO's execute() method returns either true or false. As the error already tells you: fetchAll() expects an object. Using three lines of code would be the shortest way.
Another option is to use an ORM like propel, it works really smooth and will save you alot of time.

PDO quote method

Where and when do you use the quote method in PDO? I'm asking this in the light of the fact that in PDO, all quoting is done by the PDO object therefore no user input should be escaped/quoted etc. This makes one wonder why worry about a quote method if it's not gonna get used in a prepared statement anyway?
When using Prepared Statements with PDO::prepare() and PDOStatement::execute(), you don't have any quoting to do : this will be done automatically.
But, sometimes, you will not (or cannot) use prepared statements, and will have to write full SQL queries and execute them with PDO::exec() ; in those cases, you will have to make sure strings are quoted properly -- this is when the PDO::quote() method is useful.
While this may not be the only use-case it's the only one I've needed quote for. You can only pass values using PDO_Stmt::execute, so for example this query wouldn't work:
SELECT * FROM tbl WHERE :field = :value
quote comes in so that you can do this:
// Example: filter by a specific column
$columns = array("name", "location");
$column = isset($columns[$_GET["col"]]) ? $columns[$_GET["col"]] : $defaultCol;
$stmt = $pdo->prepare("SELECT * FROM tbl WHERE " . $pdo->quote($column) . " = :value");
$stmt->execute(array(":value" => $value));
$stmt = $pdo->prepare("SELECT * FROM tbl ORDER BY " . $pdo->quote($column) . " ASC");
and still expect $column to be filtered safely in the query.
The PDO system does not have (as far as I can find) any mechanism to bind an array variable in PHP into a set in SQL. That's a limitation of SQL prepared statements as well... thus you are left with the task of stitching together your own function for this purpose. For example, you have this:
$a = array(123, 'xyz', 789);
You want to end up with this:
$sql = "SELECT * FROM mytable WHERE item IN (123, 'xyz', 789)";
Using PDO::prepare() does not work because there's no method to bind the array variable $a into the set. You end up needing a loop where you individually quote each item in the array, then glue them together. In which case PDO::quote() is probably better than nothing, at least you get the character set details right.
Would be excellent if PDO supported a cleaner way to handle this. Don't forget, the empty set in SQL is a disgusting special case... which means any function you build for this purpose becomes more complex than you want it to be. Something like PDO::PARAM_SET as an option on the binding, with the individual driver deciding how to handle the empty set. Of course, that's no longer compatible with SQL prepared statements.
Happy if someone knows a way to avoid this difficulty.
A bit late anwser, but one situation where its useful is if you get a load of data out of your table which you're going to put back in later.
for example, i have a function which gets a load of text out of a table and writes it to a file. that text might later be inserted into another table. the quote() method makes all the quotes safe.
it's real easy:
$safeTextToFile = $DBH->quote($textFromDataBase);

converting raw queries to prepared statement

suppose I have my 1995 fashion function meant to send queries to mysql.
I have lots of queries on my project and I'm looking for a function/class able to parse the raw query (suppose: SELECT foo from bar where pizza = 'hot' LIMIT 1)
and create a prepared statement with php. do you have any tips on that?
is it worth it? or it's better to just rewrite all the queries?
I can count 424 queries on my project, and that's just SELECTs
thanks for any help
Try this:
function prepare1995Sql_EXAMPLE ($sqlString) {
# regex pattern
$patterns = array();
$patterns[0] = '/\'.*?\'/';
# best to use question marks for an easy example
$replacements = array();
$replacements[0] = '?';
# perform replace
$preparedSqlString = preg_replace($patterns, $replacements, $sqlString);
# grab parameter values
$pregMatchAllReturnValueHolder = preg_match_all($patterns[0], $sqlString, $grabbedParameterValues);
$parameterValues = $grabbedParameterValues[0];
# prepare command:
echo('$stmt = $pdo->prepare("' . $preparedSqlString . '");');
echo("\n");
# binding of parameters
$bindValueCtr = 1;
foreach($parameterValues as $key => $value) {
echo('$stmt->bindParam(' . $bindValueCtr . ", " . $value . ");");
echo("\n");
$bindValueCtr++;
}
# if you want to add the execute part, simply:
echo('$stmt->execute();');
}
# TEST!
$sqlString = "SELECT foo FROM bar WHERE name = 'foobar' or nickname = 'fbar'";
prepare1995Sql_EXAMPLE ($sqlString);
Sample output would be:
$stmt = $pdo->prepare("SELECT foo FROM bar WHERE name = ? or nickname = ?");
$stmt->bindParam(1, 'foobar');
$stmt->bindParam(2, 'fbar');
$stmt->execute();
This would probably work if all your sql statements are similar to the example, conditions being strings. However, once you require equating to integers, the pattern must be changed. This is what I can do for now.. I know it's not the best approach at all, but for a sample's sake, give it a try :)
I would recommend regexp search for these queries(i think they should have pattern), later sort them and see which ones are similar/could be grouped.
Also if you have some kind of log, check which ones are executed most frequently, it doesnt make much sense to move rare queries to prepared statements.
Honestly, you should rewrite your queries. Using regular expressions would work, but you might find that some queries can't be handled by a pattern. The problem is there's alot of complexity in queries for just one pattern to parse'em all. Also, it would be best practice and consistent for your code to simply do the work and rewrite your queries.
Best of luck!
You might want to enable a trace facility and capture the SQL commands as they are sent to the database. Be forewarned, that what you are about to see will scare the pants off you:)

php zend framework log mysql queries

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

Categories