Question

I'm getting very frustrating results when trying to bind parameters to a PDO prepared statement.

The result is this error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''items'' at line 1

The error clearly shows that the table 'items' is being quoted with single quotes when it shouldn't be. I've put together a little test below. As you can see, I'm not placing the parameters in quotes in the statement to begin with, which would probably be most people's initial guess. I know it has something to do with the BindParam/BindValue (I've tried both with same results) function because if you bypass the bindParams function by setting $params to null and replacing ':table' with 'items' in the statement, it works perfectly fine.

<?php

echo 'started test...';

//connect to database

try {
    $dbHandle = new PDO('mysql:dbname=mydatabase;host=mysql.mywebsite.com', 'myuser', 'mypass');   
} catch (PDOException $e) {
    echo 'Database connection failed: ' . $e->getMessage();    
}

//print out the contents of table 'items'

print_r(query("SELECT * FROM :table", array("table" => "items"), $dbHandle));

//the query() function used above

function query($query_str, $params = null, $dbHandle) {

    $stmt_obj = $dbHandle->prepare($query_str);

    if($params != null) {           
        bindParams($stmt_obj, $params);
    }

    $stmt_obj->execute();

    //debug stuff
    echo '<pre>';   
    echo 'ERROR: ';
    $error = $stmt_obj->errorInfo();        
    echo $error[2].'<br /><br />';
    echo 'DEBUG DUMP:<br />';
    $stmt_obj->debugDumpParams();
    echo '</pre>';

    if (preg_match("/SELECT/i", $query_str)) {
        $result = array();      
        while ($row = $stmt_obj->fetch(PDO::FETCH_ASSOC)) {         
            array_push($result, $row);
        }
        unset($stmt_obj);
        return $result;
    }   

}

function bindParams($stmt, $params) {   
    if(is_object($stmt) && ($stmt instanceof PDOStatement))
    {           
        foreach($params as $key => $value)
        {            
            if(is_int($value)) {
                $param = PDO::PARAM_INT; 
            } elseif(is_bool($value)) {
                $param = PDO::PARAM_BOOL;
            } elseif(is_null($value)) {
                $param = PDO::PARAM_NULL;
            } elseif(is_string($value)) {
                $param = PDO::PARAM_STR;
            } else {
                $param = FALSE;
            }
            if($param) {                                                        
                $stmt->bindValue(":$key", $value, $param);                  
            }
        }
    }

Anyone care to put me out of my misery and point out something really obvious that I'm missing?

Was it helpful?

Solution

The symbols in prepared statements are only placeholders for values, not identifiers, so you can't create a prepared statement with a dynamic column or table name. All database references need to be resolved at prepare-time. Even if this were not true, how would PDO/MySQL know whether the :table placeholder was meant to be a literal or an identifier? They have different quoting rules.

This restriction is common to prepared statements in all database systems and all APIs. It is certainly not a PDO bug.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top