Am I losing my mind, or does the Postgres PDO driver just not support prepared statements, but instead emulates them client side?
The following code returns NO ERROR for the prepare() call, even though it should. Instead, it returns the applicable error when execute() is called.
Edit: Since according to Daniel Vérité I'm wrong, I added his suggested code. I still get the error. My code now looks like the below, with Daniel's line added.
<?php
$pdo = new PDO("pgsql:host=myhost;dbname=mydatabase");
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); // as suggested by Daniel
$sth = $pdo->prepare('COMPLETE GARBAGE');
echo "[prepare] errorInfo = " . print_r($sth->errorInfo(), true);
$sth->execute();
echo "[execute] errorInfo = " . print_r($sth->errorInfo(), true);
PHP version 5.3.15, PHP Postgres client version 9.1.4, Postgres server version 9.2.1.
See http://www.php.net/manual/en/pdo.prepare.php
Note:
Emulated prepared statements does not communicate with the database
server so PDO::prepare() does not check the statement.
(in fact real prepared statements are not sent immediately anyway, see answer to Q2 below)
Anyway you may issue:
$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);
to get real prepared statements implemented with the SQL PREPARE command.
See http://www.php.net/manual/en/pdo.setattribute.php for more.
On further discussion and tests, two questions arise:
Q1. Why does pdo::getAttribute(PDO::ATTR_EMULATE_PREPARES) yield an error?
Indeed setAttribute doesn't error out but getAttribute(PDO::ATTR_EMULATE_PREPARES) says:
'SQLSTATE[IM001]: Driver does not support this function: driver does
not support that attribute'
Looking at the documentation for pdo::getAttribute, it says The constants that apply to database connections are as follows, and a number of constants follow from PDO::ATTR_AUTOCOMMIT to PDO::ATTR_TIMEOUT, and it's remarkable that PDO::ATTR_EMULATE_PREPARES is not in them. So strictly speaking, we should not expect getAttribute(PDO::ATTR_EMULATE_PREPARES) to work, anyway.
Now looking at the source code to be sure, it appears that the pdo_pgsql driver provides a pdo_pgsql_get_attribute function that has a switch statement on:
PDO_ATTR_CLIENT_VERSION
PDO_ATTR_SERVER_VERSION
PDO_ATTR_CONNECTION_STATUS
PDO_ATTR_SERVER_INFO
and that's it. No trace of PDO_ATTR_EMULATE_PREPARES which is why ultimately this error appears.
On the other hand, the function pdo_pgsql_set_attr has a switch statement on:
PDO_ATTR_EMULATE_PREPARES
PDO_PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT
which confirms that this attribute is actually taken into account when set.
So PDO is just inconsistent with getAttribute that doesn't match setAttribute.
Q2 - When prepare emulation is false, why doesn't a bogus statement immediately raise an error when prepared?
Consider this piece of code in pgsql_statement.c:
if (!S->is_prepared) {
stmt_retry:
/* we deferred the prepare until now, because we didn't
* know anything about the parameter types; now we do */
S->result = PQprepare(H->server, S->stmt_name, S->query,
stmt->bound_params ? zend_hash_num_elements(stmt->bound_params) : 0,
S->param_types);
It shows that PQprepare is used (so that's a "real" prepared statement), but also that the statement is not immediately sent to the server. That's why the dbo::prepare("bogus statement") won't return false: it's actually not sent to the server for lack of parameter types.
Related
What is this?
This is a list of frequently asked questions regarding PHP Data Objects
Why is this?
As PDO has some features unknown to a regular PHP user, questions regarding prepared statements and error handling in PDO are quite frequent. So, this is just a place where all them can be found.
What should I do here?
If your question has been closevoted with this list, please find your question below and apply the fix to your code. It is also a good idea to take a brief look to other questions, to make yourself prepared for other common pitfalls.
The List
PDO query fails but I can't see any errors. How to get an error message from PDO?
How can I use prepared statements with LIKE operator?
How can I create a prepared statement for IN () operator?
Can I use a PDO prepared statement to bind an identifier (a table or field name) or a syntax keyword?
PDO prepared statement causes an error in LIMIT statement
See also
Reference - What does this symbol mean in PHP?
Reference - What does this error mean in PHP?
Reference - What is variable scope, which variables are accessible from where and what are “undefined variable” errors?
PDO query fails but I can't see any errors. How to get an error message from PDO?
To be able to see database errors, one have to set PDO errmode to exceptions. Exceptions are better than regular errors in many ways: they always contains a stack trace, they can be caught using try..catch or handled using dedicated error handler. And even unhandled, they act as regular PHP errors providing all the important information, following site-wide error reporting settings.
Note that setting this mode as a connection option will let PDO throw exceptions on connection errors too, which is very important.
So, here is an example for creating a PDO connection right way:
$dsn = "mysql:host=$host;dbname=$db;charset=utf8";
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
// other options
);
$pdo = new PDO($dsn, $user, $pass, $opt);
Connecting this way, you will be always notified of all database errors, occurred during query execution. Note that you have to be able to see PHP errors in general. On a live site you have to peek into error logs, so, settings have to be
error_reporting(E_ALL);
ini_set('display_errors',0);
ini_set('log_errors',1);
while on a local development server it's ok to make errors on screen:
error_reporting(E_ALL);
ini_set('display_errors',1);
and of course you should never ever use error suppression operator (#) in front of your PDO statements.
Also, due to many bad examples telling you to wrap every PDO statement into try..catch block, I have to make a distinct note:
DO NOT use try..catch operator just to echo an error message. Uncaught exception is already excellent for this purpose, as it will act just the same way as other PHP errors - so, you can define the behavior using site-wide settings - so, you will have your error message without this useless code. While unconditionally echoed error message may reveal some sensitive information to a potential attacker, yet confuse a honest visitor.
A custom exception handler could be added later, but not required. Especially for new users, it is recommended to use unhandled exceptions, as they are extremely informative, helpful and secure.
Use try..catch only if you are going to handle the error itself - say, to rollback a transaction.
PDO prepared statement causes an error in LIMIT clause
For compatibility purposes, PDO will just emulate prepared statements by substituting placeholders with actual data, instead of sending them to the server separately, unless told otherwise. And with "lazy" binding (using array in execute()), PDO will treat every parameter as a string. As a result, the prepared LIMIT ?,? query becomes LIMIT '10', '10' which is invalid syntax that causes query to fail.
This issue can be solved either
by turning emulation mode off (as MySQL can sort all placeholders properly):
$conn->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
by binding and setting proper type (PDO::PARAM_INT) explicitly:
$stm = $pdo->prepare('SELECT * FROM table LIMIT ?, ?');
$stm->bindValue(1, $limit_from,PDO::PARAM_INT);
$stm->bindValue(2, $per_page,PDO::PARAM_INT);
$stm->execute();
$data = $stm->fetchAll();
How can I use prepared statements with LIKE operator?
Prepared statement can represent complete data literal only. Not a part of literal, nor a complex expression, nor identifier. But either string or number only. So, a very common pitfall is a query like this:
$sql = "SELECT * FROM t WHERE column LIKE '%?%'";
If you ponder on this query a bit, you'd understand that being inside of single quotes, a question mark become a literal question mark, without any special meaning for the prepared statements.
So, one have to send complete string literal using prepared statement. There are 2 possible ways:
either prepare FULL expression first:
$name = "%$name%";
$stm = $pdo->prepare("SELECT * FROM table WHERE name LIKE ?");
$stm->execute(array($name));
$data = $stm->fetchAll();
or use a concatenation inside the query
$sql = "SELECT * FROM t WHERE column LIKE concat('%',?,'%')";
though the latter seems too bloated.
What is this?
This is a list of frequently asked questions regarding PHP Data Objects
Why is this?
As PDO has some features unknown to a regular PHP user, questions regarding prepared statements and error handling in PDO are quite frequent. So, this is just a place where all them can be found.
What should I do here?
If your question has been closevoted with this list, please find your question below and apply the fix to your code. It is also a good idea to take a brief look to other questions, to make yourself prepared for other common pitfalls.
The List
PDO query fails but I can't see any errors. How to get an error message from PDO?
How can I use prepared statements with LIKE operator?
How can I create a prepared statement for IN () operator?
Can I use a PDO prepared statement to bind an identifier (a table or field name) or a syntax keyword?
PDO prepared statement causes an error in LIMIT statement
See also
Reference - What does this symbol mean in PHP?
Reference - What does this error mean in PHP?
Reference - What is variable scope, which variables are accessible from where and what are “undefined variable” errors?
PDO query fails but I can't see any errors. How to get an error message from PDO?
To be able to see database errors, one have to set PDO errmode to exceptions. Exceptions are better than regular errors in many ways: they always contains a stack trace, they can be caught using try..catch or handled using dedicated error handler. And even unhandled, they act as regular PHP errors providing all the important information, following site-wide error reporting settings.
Note that setting this mode as a connection option will let PDO throw exceptions on connection errors too, which is very important.
So, here is an example for creating a PDO connection right way:
$dsn = "mysql:host=$host;dbname=$db;charset=utf8";
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
// other options
);
$pdo = new PDO($dsn, $user, $pass, $opt);
Connecting this way, you will be always notified of all database errors, occurred during query execution. Note that you have to be able to see PHP errors in general. On a live site you have to peek into error logs, so, settings have to be
error_reporting(E_ALL);
ini_set('display_errors',0);
ini_set('log_errors',1);
while on a local development server it's ok to make errors on screen:
error_reporting(E_ALL);
ini_set('display_errors',1);
and of course you should never ever use error suppression operator (#) in front of your PDO statements.
Also, due to many bad examples telling you to wrap every PDO statement into try..catch block, I have to make a distinct note:
DO NOT use try..catch operator just to echo an error message. Uncaught exception is already excellent for this purpose, as it will act just the same way as other PHP errors - so, you can define the behavior using site-wide settings - so, you will have your error message without this useless code. While unconditionally echoed error message may reveal some sensitive information to a potential attacker, yet confuse a honest visitor.
A custom exception handler could be added later, but not required. Especially for new users, it is recommended to use unhandled exceptions, as they are extremely informative, helpful and secure.
Use try..catch only if you are going to handle the error itself - say, to rollback a transaction.
PDO prepared statement causes an error in LIMIT clause
For compatibility purposes, PDO will just emulate prepared statements by substituting placeholders with actual data, instead of sending them to the server separately, unless told otherwise. And with "lazy" binding (using array in execute()), PDO will treat every parameter as a string. As a result, the prepared LIMIT ?,? query becomes LIMIT '10', '10' which is invalid syntax that causes query to fail.
This issue can be solved either
by turning emulation mode off (as MySQL can sort all placeholders properly):
$conn->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
by binding and setting proper type (PDO::PARAM_INT) explicitly:
$stm = $pdo->prepare('SELECT * FROM table LIMIT ?, ?');
$stm->bindValue(1, $limit_from,PDO::PARAM_INT);
$stm->bindValue(2, $per_page,PDO::PARAM_INT);
$stm->execute();
$data = $stm->fetchAll();
How can I use prepared statements with LIKE operator?
Prepared statement can represent complete data literal only. Not a part of literal, nor a complex expression, nor identifier. But either string or number only. So, a very common pitfall is a query like this:
$sql = "SELECT * FROM t WHERE column LIKE '%?%'";
If you ponder on this query a bit, you'd understand that being inside of single quotes, a question mark become a literal question mark, without any special meaning for the prepared statements.
So, one have to send complete string literal using prepared statement. There are 2 possible ways:
either prepare FULL expression first:
$name = "%$name%";
$stm = $pdo->prepare("SELECT * FROM table WHERE name LIKE ?");
$stm->execute(array($name));
$data = $stm->fetchAll();
or use a concatenation inside the query
$sql = "SELECT * FROM t WHERE column LIKE concat('%',?,'%')";
though the latter seems too bloated.
I'm currently working with PHP 5.4.x and SQL Server 7 and I'm having TONS of issues with the PDO object for the ODBC Driver (Which is the only one that works on Sql Server 7), Statements throw errors everywhere ....
I finally got it working using PDO::query() method, BUT I need to escape the Input .... And PDO::quote IS NOT WORKING, I red the Documentation on php pdo docs about PDO and it says that PDO::quote is Not well implemented on PDO_ODBC, which might explain why im getting errors.
For Example: this
$escapedString = $pdoObject->quote($myQueryString);
returns False, it does not return the escaped string.
That been said,
Do you know a good way to escape input to prevent SQL INJECTION???
PS: Due to driver issues (old tech) I CANNOT Trust in SQL Statements, so is not an option.
Any ideas??
EDIT:
For Example. This does not work
getQueryFromFile is only retrieving a query from a file.
and SqlServerPdo is just a wrapper class I wrote over the PHP PDO so I get the connection as a Singleton
For the Record, the query actually WORKS, it has been tested on the Sql Server Engine
$conn = SqlServerPdo::connect();
$query = SqlServerPdo::getQueryFromFile('STUDENTS_FIND');
$statement = $conn->prepare($query);
$statement->bindParam(':id', $id, PDO::PARAM_INT);}
$statement->execute();
This throws the error:
text is incompatible with int (SQLExecute[206] at ext\pdo_odbc\odbc_stmt.c:133)
It seems as if the statement is treating the :id param as a text, not as an INT.
bindValue returns the same error
What is this?
This is a list of frequently asked questions regarding PHP Data Objects
Why is this?
As PDO has some features unknown to a regular PHP user, questions regarding prepared statements and error handling in PDO are quite frequent. So, this is just a place where all them can be found.
What should I do here?
If your question has been closevoted with this list, please find your question below and apply the fix to your code. It is also a good idea to take a brief look to other questions, to make yourself prepared for other common pitfalls.
The List
PDO query fails but I can't see any errors. How to get an error message from PDO?
How can I use prepared statements with LIKE operator?
How can I create a prepared statement for IN () operator?
Can I use a PDO prepared statement to bind an identifier (a table or field name) or a syntax keyword?
PDO prepared statement causes an error in LIMIT statement
See also
Reference - What does this symbol mean in PHP?
Reference - What does this error mean in PHP?
Reference - What is variable scope, which variables are accessible from where and what are “undefined variable” errors?
PDO query fails but I can't see any errors. How to get an error message from PDO?
To be able to see database errors, one have to set PDO errmode to exceptions. Exceptions are better than regular errors in many ways: they always contains a stack trace, they can be caught using try..catch or handled using dedicated error handler. And even unhandled, they act as regular PHP errors providing all the important information, following site-wide error reporting settings.
Note that setting this mode as a connection option will let PDO throw exceptions on connection errors too, which is very important.
So, here is an example for creating a PDO connection right way:
$dsn = "mysql:host=$host;dbname=$db;charset=utf8";
$opt = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
// other options
);
$pdo = new PDO($dsn, $user, $pass, $opt);
Connecting this way, you will be always notified of all database errors, occurred during query execution. Note that you have to be able to see PHP errors in general. On a live site you have to peek into error logs, so, settings have to be
error_reporting(E_ALL);
ini_set('display_errors',0);
ini_set('log_errors',1);
while on a local development server it's ok to make errors on screen:
error_reporting(E_ALL);
ini_set('display_errors',1);
and of course you should never ever use error suppression operator (#) in front of your PDO statements.
Also, due to many bad examples telling you to wrap every PDO statement into try..catch block, I have to make a distinct note:
DO NOT use try..catch operator just to echo an error message. Uncaught exception is already excellent for this purpose, as it will act just the same way as other PHP errors - so, you can define the behavior using site-wide settings - so, you will have your error message without this useless code. While unconditionally echoed error message may reveal some sensitive information to a potential attacker, yet confuse a honest visitor.
A custom exception handler could be added later, but not required. Especially for new users, it is recommended to use unhandled exceptions, as they are extremely informative, helpful and secure.
Use try..catch only if you are going to handle the error itself - say, to rollback a transaction.
PDO prepared statement causes an error in LIMIT clause
For compatibility purposes, PDO will just emulate prepared statements by substituting placeholders with actual data, instead of sending them to the server separately, unless told otherwise. And with "lazy" binding (using array in execute()), PDO will treat every parameter as a string. As a result, the prepared LIMIT ?,? query becomes LIMIT '10', '10' which is invalid syntax that causes query to fail.
This issue can be solved either
by turning emulation mode off (as MySQL can sort all placeholders properly):
$conn->setAttribute( PDO::ATTR_EMULATE_PREPARES, false );
by binding and setting proper type (PDO::PARAM_INT) explicitly:
$stm = $pdo->prepare('SELECT * FROM table LIMIT ?, ?');
$stm->bindValue(1, $limit_from,PDO::PARAM_INT);
$stm->bindValue(2, $per_page,PDO::PARAM_INT);
$stm->execute();
$data = $stm->fetchAll();
How can I use prepared statements with LIKE operator?
Prepared statement can represent complete data literal only. Not a part of literal, nor a complex expression, nor identifier. But either string or number only. So, a very common pitfall is a query like this:
$sql = "SELECT * FROM t WHERE column LIKE '%?%'";
If you ponder on this query a bit, you'd understand that being inside of single quotes, a question mark become a literal question mark, without any special meaning for the prepared statements.
So, one have to send complete string literal using prepared statement. There are 2 possible ways:
either prepare FULL expression first:
$name = "%$name%";
$stm = $pdo->prepare("SELECT * FROM table WHERE name LIKE ?");
$stm->execute(array($name));
$data = $stm->fetchAll();
or use a concatenation inside the query
$sql = "SELECT * FROM t WHERE column LIKE concat('%',?,'%')";
though the latter seems too bloated.
PHP's PDO allows multiple querys to be executed at once, either via the query() method or as a prepared statement. Both of the following examples work:
// Two SQL queries
$query = "SELECT * FROM table; DROP table;"
// Execute via query()
$pdo->query($query);
// Execute via prepared statement
$stmt = $pdo->prepare($query);
$stmt->execute();
Is there any way to limit PDO to a single query at a time, much like the mysql_query() function is?
This is a more up-to-date answer to this question.
The old way of preventing multi query execution was to disable emulated prepares, however this was only applicable to the PDO::prepare() method. In newer versions of PHP (>= 5.5.21 and >= 5.6.5), a new constant has been introduced to disable this multi query execution in both PDO::prepare() and PDO::query(). (Constants aren't usually added in patch versions, but this was done due to the severity of a Drupal SQL injection attack brought about by this capability).
The new constant is PDO::MYSQL_ATTR_MULTI_STATEMENTS and must be set on object creation (as the fourth argument to the PDO constructor) - setting it on a pre-existing object with PDO::setAttribute() will not work.
$pdo = new PDO('mysql:host=_;dbname=_', '', '', [PDO::MYSQL_ATTR_MULTI_STATEMENTS => false]);
Mmm, there's a way of achieving this by disabling the emulation of prepared statements in PDO to make it use the native mysql API instead (multi-querying is not supported in server-side prepared statements):
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
However, one of the drawbacks of this option is that the query cache is lost.