ffgpga08
ffgpga08

Reputation: 154

"number of bound variables does not match number of tokens" -- but it does

I’ve seen many people get the error SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens, but as far as I can tell, none of their problems helped or applied to me. I’m convinced it’s something really stupid on my part...

When I echo out $ps and $ps_val (both in my db class and on the page), they have the same number of values. I’ll even echo out the SQL and count and double check spelling of the placeholders and it all adds up. I even went into phpMyAdmin, copy-pasted my SQL, and copy-pasted all placeholders with their values, and that worked, so I know it’s not SQL syntax.

$ps[] = ":category";
$ps_val[] = $_GET['cat'];

if (isset($_GET['color'])){
    // resort_color converts hex color into comma delimited text colors
    // e.g. resort_color("#000000") returns "black, black with white trim, etc."
    $colors = resort_color($_GET['color']);

    if (strpos($colors, ",") >= 0){
        $colors = explode(",", $colors);

        $i = 0;
        foreach ($colors as $color){
            $color = trim($color);
            if ($i == 0){
                $sql_color = " AND `Color` LIKE \"%:color{$i}%\"";
                $ps[] = ":color{$i}";
                $ps_val[] = $color;
            } else {
                $sql_color = $sql_color . " OR `Color` LIKE \"%:color{$i}%\"";
                $ps[] = ":color{$i}";
                $ps_val[] = $color;
            }
            $i++;
        }
    } else {
        $sql_color = 'AND `Color` LIKE \"%:color%\"';
        $ps[] = ":color";
        $ps_val[] = $colors;
    }
} else {
    $sql_color = "";
}

$sql[] = 'SELECT * FROM `bullet_catalog` WHERE `Category` = :category ' . $sql_color;
$sql[] = 'SELECT * FROM `leeds_catalog` WHERE `Category` = :category ' .  $sql_color;
$sql[] = 'SELECT * FROM `trimark_catalog` WHERE `Category` = :category ' .  $sql_color;

if ($db->query_all($sql, $ps, $ps_val)){
    $products = $db->query_all($sql, $ps, $ps_val);
} else {
    $products = "";
}

And my query_all function:

public function query_all($sql, $ps="", $ps_val=""){
    if (!$sql){
        return false;
    } else {
        foreach ($sql as $ind_sql){
            $query = $this->pdo->prepare($ind_sql);
            if ($ps){
                if (is_array($ps)){
                    $i = 0;
                    foreach ($ps as $pss){
                        $query->bindValue($pss, $ps_val[$i]);
                        $i++;
                    }
                } else {
                    $query->bindValue($ps, $ps_val);
                }
            }

            try {
                $query->execute();

                $result = $query->fetchAll(PDO::FETCH_ASSOC);
                foreach ($result as $res){
                    $results[] = $res;
                }   
            } catch (PDOException $error){
                echo "<div><pre>" . print_r($error->getMessage(), true) . "</pre></div>";
            }
        }
        if (isset($results)){
            return $results;
        } else {
            return false;
        }
    }
}

PDOStatement::debugDumpParams returns

SQL: [295] SELECT * 
FROM `table` 
WHERE `Category` = ":category" 
AND `Color` LIKE "%:color0%" 
OR `Color` LIKE "%:color1%" 
OR `Color` LIKE "%:color2%" 
OR `Color` LIKE "%:color3%" 
OR `Color` LIKE "%:color4%" 
OR `Color` LIKE "%:color5%" 
OR `Color` LIKE "%:color6%" 

Params: 8 

Key: Name: [9] :category 
paramno=-1 
name=[9] ":category" 
is_param=1 param_type=2 

Key: Name: [7] :color0 
paramno=-1 
name=[7] ":color0" 
is_param=1 param_type=2 

Key: Name: [7] :color1 
paramno=-1 
name=[7] ":color1" 
is_param=1 param_type=2 

Key: Name: [7] :color2 
paramno=-1 
name=[7] ":color2" 
is_param=1 param_type=2 

Key: Name: [7] :color3 
paramno=-1 
name=[7] ":color3" 
is_param=1 param_type=2 

Key: Name: [7] :color4 
paramno=-1 
name=[7] ":color4" 
is_param=1 param_type=2 

Key: Name: [7] :color5 
paramno=-1 
name=[7] ":color5" 
is_param=1 param_type=2 

Key: Name: [7] :color6 
paramno=-1 
name=[7] ":color6" 
is_param=1 param_type=2 

Upvotes: 1

Views: 288

Answers (1)

Michal Brašna
Michal Brašna

Reputation: 2323

If you use PDO bindValue to do a search with a LIKE condition you cannot put the percentages and quotes to the param placeholder '%:color0%'.

This is WRONG:

SELECT * FROM `bullet_catalog` WHERE `color` LIKE '%:color0%'

The CORRECT solution is to use just the placeholder like this:

SELECT * FROM `bullet_catalog` WHERE `color` LIKE :color0

And then add the percentages to the php variable where you store the keyword:

$query->bindValue(':color0', "%" . $color0 . "%");

Also as % and _ are not escaped in $color0, so you should escape them yourself.

Also explained

Upvotes: 1

Related Questions