Search code examples
phpmysqlnette

SQLSTATE[42000]: Syntax error or access violation [ PHP ]


I build SQL query with a method and then return it and use it.

$query = $this->buildSearchQuery($searchParams);            
return $this->db->query($query);

Unfortunately this throw me an error:

SQLSTATE[42000]: Syntax error or access violation: 1064 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 ''SELECT * FROM candidates WHERE firstname = ? AND surname = ?','Dante', 'Hickman' at line 1

I was searching for it because this looks like SQL syntax fail of previous script which build query so I did simple thing I dump this $query before I used it.

Dump return this:

"'SELECT * FROM candidates WHERE firstname = ? AND surname = ?','Dante', 'Hickman'" (81)

Which is correctly, string with 81 chars. After this, I try to put this to original query instead of variabile and it looks like this:

return $this->db->query('SELECT * FROM candidates WHERE firstname = ? AND surname = ?','Dante', 'Hickman'); 

This secod script run correcty so it looks query is build correctly, but still error. I am missing something?

I hope for any advise which can help me solve this problem.

p.s. Syntax of that query is from nette framework but system should be the same.

EDIT: adding buildSearchQuery()

function buildSearchQuery($searchParams)
    {
        $column = "";
        $values = "";
        $col = "";
        $i=0;
        // Trim to make sure user doesn't enter space there
        if((trim($searchParams->firstname)))
        {
            $column .= "firstname,";
            $i++;
        }
        if((trim($searchParams->surname)))
        {
            $column .= "surname,";  
            $i++;
        }

        if((trim($searchParams->specialization)))
        {
            $column .= "specialization,";   
            $i++;
        }           
        if($searchParams->english !== NULL)
        {
            $column .= "english,";
            $i++;
        }           
        if($searchParams->german !== NULL)
        {
            $column .= "german,";
            $i++;
        }           
        if($searchParams->russian !== NULL)
        {
            $column .= "russian,";
            $i++;
        }           
        if($searchParams->french !== NULL)
        {
            $column .= "french,";
            $i++;
        }           
        if($searchParams->school !== NULL)
        {
            $column .= "school,";
            $i++;
        }

        if((trim($searchParams->registrationDate)))
        {
            $column .= "registrationDate";
            $i++;
        }
        if($i > 0)
        {
            // If number of columns is bigger then 0 (if user fill atleast one input)                   
            $columns = explode(",", $column);       
            // Create list of values for query (name of columns and values)
            foreach($columns as $c)
            {                                                   
                if (isset($searchParams->$c)) {     
                    $values .= "'".$searchParams->{$c}."', ";               
                    $col .= $c." = ? AND ";                 
                }                       
            }
            // Remove last "," and space
            $values = substr_replace($values, "", -2);          
            $col = substr_replace($col, "", -5);    
            $query = $col."',".$values;
            $query = "'SELECT * FROM candidates WHERE ".$query;         
            //$query = substr($query, 0, -1); //remove last char ( ' in this case)
            return $query;
        }
        else
        {
            $query = "SELECT * FROM candidates";
            return $query;
        }
    }

Solution

  • The comments above are correct, you are passing a string as the only argument, instead of multiple arguments query expects.

    One possible solution is creating an array and calling the method with array items as arguments (e.g. using call_user_func_array). You can however do better.

    Nette\Database is quite powerful and it can build the query for you. When you pass an associative array like ["column1" => "value1", "column2" => "value2"] as the only argument of where method, it will create corresponding WHERE column1 = 'value1' AND column2 = 'value2' clause. And of course it will securely escape the values to prevent SQL injection.

    You can, therefore, simplify your code into something like following:

    $columns = ["firstname", "surname", "specialization", "english", "german", "russian", "french", "school", "registrationDate"];
    $conditions = [];
    foreach ($columns as $c) {
        if (isset($searchParams->$c) && trim($searchParams->$c) !== "") {
            $conditions[$c] = $searchParams->{$c};
        }
    }
    return $this->db->table('candidates')->where($conditions);
    

    No if–else statement is needed as when the array is empty, NDB correctly doesn’t append the WHERE clause.