Is there any point in saving PDO prepared statements for reuse in a session?
I'm building a site that uses MySQL fulltext queries, which I'm putting together in my PHP, like
SELECT * FROM table
WHERE MATCH (title) AGAINST ($search_string IN BOOLEAN MODE) AND
MATCH (keywords) AGAINST ($keywords IN BOOLEAN MODE)
ORDER BY $order_by $asc_desc
It seems to take a lot longer to run this kind of query when I prepare and execute it with bound parameters, than when I just prepare and execute a query string with the values included. But I need to use prepared statements to prevent the risk of SQL injection.
In any session I would very likely run the same query several times, with different parameter values. Would it make sense for me to save the PDOStatement object once it's been created (for example in the session)? If so, what would be the best way to do that? Would it be good practice to save each prepared statement in an associative array as it's created, with the SQL query string as the key for each?
On further reading I found you can't use bound params for the ORDER BY and ASC / DESC part of a statement. When I replace these with fixed values the performance improves.
There is no benefits to store prepare statement into session for reusable purpose,
the expensive cost is on the query execution itself.
If the data you are grabbing constantly changes, then caching the resultset may no longer be valid shortly after you retrieve them.
However, if your data appears to be static:
session_start();
$_SESSION['cachedResultSet'] = $fetchedResultset;
echo $_SESSION['cachedResultSet'][0][0];
# above statement `echo ...` depends on how your result is, be it an array or object
I think it would be acceptable to a degree to store resultsets in session variables, but perhaps it would depend (sorry to be redundant) whether the data changes quickly or on how often you grab this particular resultset, etc...
Related
One day I was googling to understand when a prepared statment should be used. As I searched online many claimed that you should use always prepared statments. Now the question I have is... does this also count for LIMIT? I mean it is possible (I tried) but is it really that rational? Same question on ORDER BY too.
When the database does not allow you to use a parameter on a specific location of the SQL statement you need to assemble the query on the fly, by the use of Dynamic SQL. That is... concatenating strings to get a full functioning SQL query.
Now, the trick is to make it safe against SQL Injection. To do this:
Make sure the value for LIMIT is an integer, and not something unsafe coming right from the UI like 3; DROP TABLE EMPLOYEE.
For the ORDER BY clause make sure the columns are not coming from the UI "as is"; use some kind of projection. For example, if there are 50 columns to order by, the UI can display them all, but then just send a number (from 1 to 50) to the backend; the backend receives this number and reconstitutes the ordering column(s) from it.
Normally the LIMIT parameters must be literals, not values that can be substituted for placeholders, so you would have to validate that they're integers before substituting into the string.
However, if you use PDO rather than mysqli, it allows you to perform parameter substitution in the LIMIT clause, by using the PDO::ATTR_EMULATE_PREPARES option. This is automatically enabled for a specific prepared statement if it contains parameters in this clause. See How to apply bindValue method in LIMIT clause? for the specific details.
Is there a point to use prepared statement when you just want to select all rows in a table? There are notning to bind compared if you used WHERE name = ? in the query.
$stmt->prepare("SELECT * FROM countries ORDER BY name");
Is it better to use the MySQLi without Prepared Statements like
$sql = "SELECT * FROM countries ORDER BY name";
Prepared statements are preferable to plain SQL queries when you are using parameters to dynamically generate the query. In your example, your SQL contains no variables, so using a plain query or prepared statement are functionally equivalent.
Prepared Statements
To answer your question directly, no. And in many languages the prepared statement library wouldn't even work with a query like that due to the method signatures of that library. I am going to give a background of prepare statements and database management systems because they are both essential to understanding the efficiencies generated by prepared statements.
Queries and Database's
When you execute a statement in any database management systems you trigger an execution of steps. At a very high level the database management system does the following things.
Query is received by the DBMS
SQL is interpreted
Query parsing
Query optimization
Resultset returned
Prepared Statements and Database's
A prepared statement is similar to any other SQL query issued to a database management system except with a few caveats.
First, a prepared statement template (without parametic values) is sent to the database management system from the application. After interpreting the SQL the database system then begins query parsing and query optimization where it validates the syntax and semantics, and begins the process of finding the best query plan, respectively.
A query plan is a set of executions used to run the supplied query. In most cases a query can have multiple query planes. It is part of the database management system to pick the most efficient plane.
Query optimizer continues to generate a variety of query plans and assigned then a cost and a value.
This process so far is mostly the same for both prepared statements and regular statements.
So to understand why your query would not benefit from being a prepared statement (besides the fact that prepared statements are defined as necessarily having parameters), let's take a closer look into the prepared statement optimization.
Optimizing
We stayed before that the query optimizer picks from a list of query plans for the most efficient one. However, we didn't discuss how it actually does this. For a non parametic statement the optimizer assign each plan a scalar cost value.
However, for a parametic query the optimizer associates each query plant to a function that maps multi dimensional parameter space to cost space, respectively. Basic to get all optimal queries for any given input.
This is a costly process, however on successive runs is very quick because the answer is known instead of calculates.
.
I'm working on an application at the moment that uses PDO with a MySQL database.
I'm seeing some queries, which are just very simple SELECT statements, e.g.
SELECT * FROM table ORDER BY name ASC
The code does not use prepare, for example:
$sql = "SELECT * FROM " . $this->table . " ORDER BY name ASC";
$stmt = $this->db->query($sql);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $results;
Is it ok to do this, i.e. without using prepare, if there's no placeholders in the query?
The reason I've asked this is because according to the documentation it says
The SQL statement can contain zero or more named (:name) or question mark (?) parameter markers
which makes me wonder why you'd use this in the case of having no (zero) parameter markers?
Yes, because the use of prepared statements have 2 main causes:
Enhance running the same query with different parameters.
Prevent sql injection by separating sql code from the parameters.
Since you have no parameters that could be handled by a prepared statement (table names cannot be a parameter), you do not gain anything by pushing the query through as a prepared statement.
You still need to make sure that whatever is returned by $this->table will not cause any issues with the generated sql code.
Of course you can omit prepare if there is no dynamic data in your query.
But prepared statements have more advantages than only securing your queries.
According to http://php.net/manual/en/pdo.prepared-statements.php a second advantage is, that statements can be prepared once and executed multiple times.
The query only needs to be parsed (or prepared) once, but can be executed multiple times with the same or different parameters. When the query is prepared, the database will analyze, compile and optimize its plan for executing the query. For complex queries this process can take up enough time that it will noticeably slow down an application if there is a need to repeat the same query many times with different parameters. By using a prepared statement the application avoids repeating the analyze/compile/optimize cycle. This means that prepared statements use fewer resources and thus run faster.
Nevertheless, if you run your query only once and there is no dynamic data inside your query, omitting prepare is also fine.
In reality, you have to run such a query extremely seldom. A few tables with configuration-like options may be.
In all other cases at least LIMIT clause is obligatory.
So the question is rather a theoretical one.
I have been researching into PDO's bindValue(). I know that preparing my SQL statements with PDO is keeping SQL injections from happening.
Code Example:
$stmt = $dbh->prepare('SELECT * FROM articles WHERE id = :id AND title = :title');
$stmt->bindValue(':id', PDO::PARAM_INT);
$stmt->bindValue(':title', PDO::PARAM_STR);
$stmt->execute();
By binding the ID as a number, and the Title was a string, we can limit the damage done when someone tries to do an SQL injection within the code.
Should we always bind our values with a PDO::PARAM_ so we can limit what can be pulled from the database in an SQL injection? Does this add more security with PDO when doing our bindValue()?
There are two questions in one. It is essential not to confuse them
Should we always use a placeholder to represent a variable data in the query?
Should we always use certain function in the application code to follow the above rule?
Also, from the clarification in the comments under the opening post, the third question can be seen:
Should we always use third parameter, or it's OK to let PDO bind all the parameters as strings by default?
1. For the first question the answer is absolutely and definitely - YES.
While for the second one, for sake of code sanity and DRYness -
2. Avoid manual binding when possible.
There are many ways to avoid manual binding. Some of them are:
ORM is an excellent solution for the simple CRUD operations and must have in a modern app. It will hide SQL from you completely, doing the binding behind the scenes:
$user = User::model()->findByPk($id);
Query Builder is also the way to go, disguising SQL in some PHP operators but again hiding the binding behind the scenes:
$user = $db->select('*')->from('users')->where('id = ?', $id)->fetch();
some abstraction library may take care of the passed data by means of type-hinted-placeholders, hiding the actual binding again:
$user = $db->getRow("SELECT * FROM users WHERE id =?i", $id);
if you are still using PHP in the last century ways, and have raw PDO all over the code - then you can pass your variables in execute(), still saving yourself a lot of typing:
$stmt = $dbh->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([$id]);
$user = $stmt->fetch();
As of the third question - as long as you are binding numbers as strings (but not the opposite!) -
3. It's all right with mysql, to send almost every parameter as a string
as mysql will always convert your data to the proper type. The only case known to me, is a LIMIT clause where you cannot format number as a string - thus, the only related case is one when PDO is set in emulation mode and you have to pass a parameter in LIMIT clause. In all other cases you can omit third parameter, as well as explicit call to bindValue() without any problem.
You should definitely use the prepare API and pass values separately from the query, as opposed to doing plain string interpolation (e.g. "SELECT * FROM foo WHERE bar = '$baz'" → bad).
For binding parameters, you have three options:
bindParam
bindValue
execute
It doesn't really matter which of these you use, they're all equally secure. See these answers for some details about the differences:
Confusion between bindValue() and bindParam()?
Using PDO without binding
When using bindParam or bindValue, passing the third PDO::PARAM_ argument type is optional. If you don't pass it, it defaults to binding the argument as string. That means you may end up with a query equivalent to ... WHERE foo = '42' instead of ... WHERE foo = 42. It depends on your database how it will handle this. MySQL will cast the string to a number automatically as needed, just as PHP does (e.g. in '42' + 1). Other databases may be more fussy about types.
Again, all options are equally safe. If you're trying to bind a string 'foo' using PDO::PARAM_INT, the string will be cast to an integer and accordingly bound as the value 0. There's no possibility for injection.
Yes you should always bind params with prepared statement.
It's more secure, and limit SQL injection. But this is not the only think you must do to query params: a correct type control is required, best if you map a row into an object and throw an exception in it if it has invalid data.
I hope I can be useful!
Yes, binding is the way to go. Or parameterised queries which a more generalized term.
#Theo does a wonderful job explaining why parameterized queries is the way to go
You could also use stored procedures for additional security but is an over kill if you have one application to one database. Its good for multiple applications to one database to ensure consistency when handling data
Is there any way to execute more sql prepared statements at once? Or at least use something to achieve this result, can it be emulated with transactions?
pg_send_query can execute more statements (from php docs "The SQL statement or statements to be executed.")
but
pg_send_execute and pg_send_prepare can work only with one statement.
The query parameter has the following description
"The parameterized SQL statement. Must contain only a single statement. (multiple statements separated by semi-colons are not allowed.) If any parameters are used, they are referred to as $1, $2, etc."
from http://www.php.net/manual/en/function.pg-send-prepare.php
Is there any way to send more statements at once to make less roundtrips between php and postgresql like the pg_send_query does?
I don't want to use pg_send_query because without parameter binding I can have sql injection vulnerabilities in my code.
The round trips to the DB server shouldn't be your bottleneck as long as you are (a) using persistent connections (either directly or via a pool) and (b) aren't suffering from the "n+1 selects" problem.
New connections have an order of magnitude overhead which slows things down if done on every query. The n+1 problem results in generating far more trips than is really needed if the application retrieved (or acted upon) sets of related rows rather than doing all operations one at a time.
See: What is the n+1 selects problem?
Separate your queries by semicolon:
UPDATE customers SET last_name = 'foo' WHERE id = 1;UPDATE customers SET last_name = 'bar' WHERE id = 2;
Edit:
Okay you cannot do this on the call side:
The parameterized SQL statement. Must contain only a single statement. (multiple statements separated by semi-colons are not allowed.)
Another way would be to call a stored procedure with this method and this SP issues multiple statements.