Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP Postgres PDO driver does not support prepared statement?

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.

like image 247
CXJ Avatar asked Jul 05 '13 16:07

CXJ


People also ask

Does PDO use prepared statements?

PDO will emulate prepared statements/bound parameters for drivers that do not natively support them, and can also rewrite named or question mark style parameter markers to something more appropriate, if the driver supports one style but not the other.

How fetch data from database in PHP and display PDO?

Fetch data from a result set by calling one of the following fetch methods: To return a single row from a result set as an array or object, call the PDOStatement::fetch method. To return all of the rows from the result set as an array of arrays or objects, call the PDOStatement::fetchAll method.

What type of object is returned by PDO :: prepare ()?

PDO::query() prepares and executes an SQL statement in a single function call, returning the statement as a PDOStatement object.


1 Answers

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.

like image 185
Daniel Vérité Avatar answered Oct 04 '22 03:10

Daniel Vérité