I'm using PHP, and MySQL with PDO. Sometimes I need to prepare a statement with one variable (placeholder) used more than once in this query.
Example:
SELECT * FROM messages WHERE from_id = :user OR to_id = :user
However if I will try to prepare this statement I will have an error so I need to do this in a way like this:
SELECT * FROM messages WHERE from_id = :user1 OR to_id = :user2
To call this statement I will need to have an array like this:
array('user1'=>$user_id, 'user2'=>$user_id);
It looks so stupid for me! Why MySQL (PDO?) don't allowing me to use one place holder more than once and forcing me to use extra variables which requires more control?!
This can be handled easy if the query is relatively simple (like I posted above), but now I built a query with 5 (!!!) uses of single variable. Each time I add the placeholder I need to check the code in many places to make it OK.
Is there any setting or a tweak to bypass this?
Is there any setting or a tweak to bypass this?
Yes, there is. You can turn emulation mode ON and be able to use the same placeholder multiple times.
So the described behavior is observed only when the emulation is turned OFF. I don't really understand why it is so but here is an explanation from Wez Furlong (the PDO author):
The change was made for two reasons; first and foremost, if you re-use the same variable in a bind, it is possible to induce a crash when using some drivers. It’s not possible to guarantee to do the right thing, and having a way to trigger a crash can sometimes be used as an attack vector for a security exploit.
The second reason is that of portability. Some drivers would internally perform this check and error out. If you code against the drivers that don’t enforce this, then your code won’t work on those that don’t.
http://paul-m-jones.com/archives/243#comment-740
Related
This is odd. I'm running a query with just a single INSERT, preceded by a SET statement. The query looks something like this:
SET #discount:=(SELECT discount * :applyDiscount FROM fra_cus WHERE customerID=:customerID AND franchiseID=:franchiseID);
INSERT INTO discounts_applied (unitID, franchiseID, customerID, amount)
VALUES(:unitID, :franchiseID, :customerID, #discount * :price);
It appears that if I prepare these as two separate PDO queries, lastInsertID() works fine... but if I prepare them and execute them in the same statement, lastInsertID() returns nothing.
It's not the end of the world, but it's annoying. Anyone know why this would be the case? For the record, there's a reason I need to define #discount as a variable (pertains to triggers on one of the tables). Also this is all taking place within a larger transaction.
First of all, I would strongly recommend to run every query in a distinct API call. This is how an Application Programming Interface is intended to work.
It won't only prevent situations like this but also will make your code a multitude times more readable and maintainable.
And it will make your code much safer too. You can run multiple statements in a single call only at the expense of the native prepared statements. However virtual this vulnerability is, why taking chances at all?
Why not to make a regular SELECT query instead of SET, get the resulting value into a PHP variable and then use it among other variables, just through a placeholder? I don't see any reason why there should be such a complex way to deal with simple data.
In case I failed to convince you, the reason is simple. You are running two queries, and the first one doesn't trigger any insert ids. And obviously, you need this query's metadata (errors, affected rows, whatever), not the other one's first. So you get it. And to get the second'query's metadata you have to ask a database for it. The process is explained in my article: Treating PHP delusions - The only proper PDO tutorial: Running multiple queries with PDO. Basically PDOStatement::nextRowset() is what you need.
I have many questions about PDO ...
Should I use prepare() only when I have parameters to bind? When I need to do a simple query like select * from table order by ... should i use query()?
Should I use exec() when I have update and delete operations and need to get the number of rows affected, or should I use PDOStatement->rowCount() instead?
Should I use closeCursor when I do insert, update and delete, or only with select when I need to do another select?
Does $con = NULL; really close the connection?
Is using bindParam with foreach to make multiple inserts a good point? I mean performance wise, because I think that doing (...),(...) on the same insert is better isn't it?
Can you provide me some more information (URL) about performance points when using PHP PDO MySQL? If someone has another hint it would be really useful.
When I was developing the DB layer in Zend Framework 1.0, I made it use prepare/execute for all queries by default. There is little downside to doing this.* There's a little bit of overhead on the PHP side, but on the MySQL side, prepared queries are actually faster.
My practice is to use query() for all types of queries, and call rowCount() after updates. You can also call SELECTROW_COUNT().
CloseCursor is useful in MySQL if you have pending rows from a result set, or pending result sets in a multi-result set query. It's not necessary when you use INSERT, UPDATE, DELETE.
The PDO_mysql test suite closes connections with $con=NULL and that is the proper way. This won't actually close persistent connections managed by libmysqlnd, but that's deliberate.
Executing a prepared INSERT statement one row at a time is not as fast as executing a single INSERT with multiple tuples. But the difference is pretty small. If you have a large number of rows to insert, and performance is important, you should really use LOAD DATA LOCAL INFILE. See also http://dev.mysql.com/doc/refman/5.6/en/insert-speed.html for other tips.
You can google for "PDO MySQL benchmark" (for example) to find various results. The bottom line, however, is that choosing PDO vs. Mysqli has no clear winner. The difference is slight enough that it diminishes relative to other more important optimization techniques, such as choosing the right indexes, making sure indexes fit in RAM, and clever use of application-side caching.
* Some statements cannot run as prepared statements in MySQL, but the list of such statements gets smaller with each major release. If you're still using an ancient version of MySQL that can't run certain statements with prepare(), then you should have upgraded years ago!
Re your comment:
Yes, using query parameters (e.g. with bindValue() and bindParam()) is considered the best methods for defending against SQL injections in most cases.
Note that there's an easier way to use query parameters with PDO -- you can just pass an array to execute() so you don't have to bother with bindValue() or bindParam():
$sql = "SELECT * FROM MyTable WHERE name = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute( array("Bill") );
You can also use named parameters this way:
$sql = "SELECT * FROM MyTable WHERE name = :name";
$stmt = $pdo->prepare($sql);
$stmt->execute( array(":name" => "Bill") );
Using quote() and then interpolating the result into a query is also a good way to protect against SQL injection, but IMHO makes code harder to read and maintain, because you're always trying to figure out if you have closed your quotes and put your dots in the right place. It's much easier to use parameter placeholders and then pass parameters.
You can read more about SQL injection defense in my presentation, SQL Injection Myths and Fallacies.
Most of questions can be answered with just common sense. So, here I am.
It doesn't matter actually.
Abolutely and definitely - NO. Exec doesn't utilize prepared statements. That's all.
Doesn't really matter. If you ever need this, your program architecture is probably wrong.
You can easily test it yourself. A personal experience is always preferred.
The difference considered to be negligible. However, if your multiple inserts being really slow (on INNODB with default settings for example) you have to use a transaction, which will make them fast again.
There is NONE. PDO is just an API. But APIs aren't related to performance. They just translate your commands to the service. It's either your commands or service may affect performance, but not mere API.
So, the rule of thumb is:
it's query itself that affects performance, not the way you are running it.
I’m currently working on a small set of database access and management classes in php. while trying to understand our well known mysqli better, I still fail to understand why and how variable-types defined in mysqli_stmt::bind_param could and would enhance security/utility within the current application.
The advantages of having a mysql statement going on are clear. Query and values are submitted on two different paths and that of course makes the application more secure and more error proof! But what happens to these variables… are these checked prior by php or after on in mysql?
Is it just mysqli being lazy for doing something like?
!is_numeric($v) && ( !is_null($v) || $v != “NULL”) ? “‘$v’” : $v
Or is there any var type definition on the mysql side which I don’t know?
“PREPARE stmt_name FROM … “;
“SET #var1 = 3”;
“SET #var2 = ‘foo’”;
“EXECUTE stmt_name USING #var1, #var2”;
It doesn’t seem there’s much going on this values. quite anything passed as a string is evaluated properly… then why bother?
There’s another side-question even though related to this one: is there any way to replicate mysqli’s way of sending blob string in packets?
Thanks bye
As nobody has given an answer yet... i do have one now! The data-type definition within bind_param does noting more than adding those quotes to the query, although, variables are bound at a lower level than a php script could be ever capable of! formally going though any different path apart from mysqli/pdo would mean to transfer all the data by strings!
thats it, cheers!
I'm running php 5.2.13 and i have an app that contains tons of files but they all calling one file at the beginning, i want to put some line in that file to automatically mysql real escape any query, because i don't want to go across every file and change code.
Thanks!
I don't know how well that would work. What you really need is to escape the input not things like table names, fields, etc. If you pass the entire query to an escape, I'd be willing to bet you'd find a good number of queries that will fail because it will turn things like
select * from tablename where name = 'foo'
into
select * from tablename where name = \'foo\'
Which would choke.
And, having a wrapper function in your code helps a lot (assuming you don't want to use a framework, etc). If you have "mysql_query()" littered around your code, you probably are in for a bit of work to change it up. If you can't/don't-want-to adopt a framework, at least wrap it in a function of your own, like "db_query()" like this:
function db_query($query,$and,$other,$arguments)
{
mysql_query( ... ); // you can change this to some other database later if you want
}
I did that in a project a few years ago and it helped a ton when I wanted to log some errors. I just added it to that function instead of having it in 200 places in the code.
But even that won't really help if you didn't escape input properly in the first place. In that case your only option is to take some time and fix it.
Hans has some good suggestions. But i think the bottom line is youre going to have to modify a lot of code. There is no magic bullet on this one. Whoever wrote it should have known better, and now you my friend are going to pay the price. Personally if youre going to have to go in and manually edit i would urge you to switch to PDO or mysqli. That way you can make use of prepared statements which will handle the escaping of variables for you provided you use them correctly.
If you have a large project, and need to change the data access, I would suggest to move to an ORM, my personal pick is Propel.
With that you would solve the whole escaping sql's problem, would make your app more scalable and you could also reverse your database diagram in order to generate the classes needed for Propel.
Propel will give you benefits like transactions, parameters and many more, so you should reaally think about it.
Best regards
Take note that it's not queries that you will want to escape, it's user supplied variables that are to be included in the query (unless you're writing malformed SQL yourself on purpose). So what you can do is to run mysql_real_escape_string() on, say, the $_POST array with array_map(), provided that you are not going to use that array for anything else.
mysql_real_escape_string() is still only the second best solution to the issue anyway. I you can use prepared statements (AKA parametrized queries) and you're home free.
I'm trying to update the database library that we use at work to use parameterized queries so that coworkers who are not very knowledgeable about SQL injection won't have to remember to escape input and just pass in an array of parameters instead (I'm using pg_query_params).
However, I am running into a problem. One of the requirements of the database library is that it logs each query that is executed and I can't figure out a way to get the text of a parameterized query once the parameters have been filled in. Is there any way to do this (aside from rolling my own function for parameterized queries, I guess)?
In other words, when executing a parameterized query like
pg_query_params('SELECT id FROM table WHERE foo = $1', array('bar'));
I want to get something like
SELECT id FROM table WHERE foo = 'bar'
PostgreSQL extended protocol (which is used by pg_query_params) separates parameters from queries.
The queries are ultimately constructed only on the server side, there is no way to construct them on the client side using only PostgreSQL's capabilities.
You can substitute the values of the parameters provided and log the query before sending the params using preg_replace.
Update:
You can enable PostgreSQL logging which will log the queries into a file or syslog (including bound parameters) on the server side, then periodically load this file into a PostgreSQL table.
Unless you can replace every occurrence of pg_query_params() in your source code with your own wrapper function (if you can, then you might want to use the auto prepend functionality in PHP to include the declaration of the wrapper) then you can only really do this at the database level.
C.