6

I have a function (see bottom) in PHP, which queries a MySQL database. When I use the following values:

  • $map => 1,
  • $limit => 10,
  • $from => 0,
  • $to => CURRENT_TIMESTAMP

With the SQL statement:

SELECT user,
       scoreVal AS score,
       UNIX_TIMESTAMP(timestamp) AS timestamp 
  FROM Score 
 WHERE timestamp >= :from 
   AND timestamp <= :to 
   AND map = :map 
 ORDER BY scoreVal DESC, timestamp ASC 
 LIMIT :limit

In phpMyAdmin, I get the following result:

phpMyAdmin result

However the PHP PDO gets returned an empty array.

My attempts to debug so far:

  • I have replaced he prepared SQL query with static values instead of placeholders - Returns correctly
  • Trying each placeholder separately, replacing the rest with tested hard-coded values - Returns nothing
  • Instead of passing variables to placeholders I pass fixed constants in the execute(Array()) part. - Returns nothing.
  • I have furthermore discovered after turning on mySQL query logs, that the PHP client Connects, but then quits without sending any queries.

From this, I believe it to be a problem with the place holders within the function, however I have been unable to find a reason why they are failing. This is most probably occurring on the PHP side, as no errors are being thrown by MySQL to the error file.

This is the function I am using, with the variables being passed in:

  • $map => 1,
  • $limit => 10,
  • $from => 0,
  • $to => 0

Function:

/**
 * Gets the highscore list for the map in that timespan
 * @param  integer $map   Id of map for which to fetch the highscore
 * @param  integer $limit Maximum no. of records to fetch
 * @param  integer $from  Timestamp from when to find rank
 * @param  integer $to    Timestamp up to when to find rank
 * @return array   Array of highscores arranged by rank for the map in the format [{"user"=>$user,"score"=>score,"timestamp" => timestamp}]
 */
function get_highscore_list($map,$limit,$from,$to){
    $sql = "SELECT user,scoreVal AS score,UNIX_TIMESTAMP(timestamp) AS timestamp FROM Score WHERE timestamp >= :from AND timestamp <= :to AND map = :map ORDER BY scoreVal DESC, timestamp ASC LIMIT :limit";
    if ($to==intval(0)){
        $max =1;
        $sql = str_replace(":to","NOW()",$sql,$max);
    }
    try{
    $conn = request_connection();
    $stmt = $conn->prepare($sql);
    $stmt->execute(array(':map'=>$map,':from'=>$from,':limit'=>$limit));
    $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
    }catch(PDOException $e){
        $_POST["exception"]=$e;
        continue;
    }
    return $result;
}

EDITS


Format of MySQL table:

MySQL table format


I have tried outputting the $conn->errorInfo();, however as no error is being thrown, I get returned an array of values: [00000,null,null]


The request_connection function only returns the result of this function, and it is working for all of my other statements.

/**
 * Creates a new PDO connection to the database specified in the configuration file
 * @author Ignacy Debicki
 * @return PDO A new open PDO connection to the database
 */
function create_connection(){
    try {
        $config = parse_ini_file('caDB.ini');
        $conn = new PDO('mysql' . ':host=' . $config['dbHost'] . ';dbname=' . $config['db'],$config['dbPHPUser'], $config['dbPHPPass']);
        date_default_timezone_set($config['dbTimezone']);
        return $conn;
    } catch(PDOException $e){
        throw new Exception("Failed to initiate connection",102,$e);
    }   
}

Thanks

Ignacy Debicki
  • 437
  • 4
  • 18
  • What is the data type of column `timestamp`? – randrade86 Jan 07 '16 at 17:27
  • @RodolfoAndrade See updated question – Ignacy Debicki Jan 07 '16 at 17:29
  • I've deleted my answer to your question. I assumed that you were using a UNIX timestamp (`INT`) for your table, and not a `timestamp` column. – helmbert Jan 07 '16 at 17:35
  • I have tried removing the LIMIT part of the statement as well. Still doesn't return the expected result. Either way, I also use a parametrised LIMIT clause that does work correctly – Ignacy Debicki Jan 07 '16 at 17:38
  • `echo '
    '.print_r($conn->errorInfo(), TRUE).'
    ';` should help...
    – MonkeyZeus Jan 07 '16 at 17:40
  • @MonkeyZeus I have tried outputting the `$conn->errorInfo();`, however as no error is being thrown, I get returned an array of values: `[00000,null,null]` – Ignacy Debicki Jan 07 '16 at 17:44
  • Where did you put the `errorInfo()` check? Update your code please. – MonkeyZeus Jan 07 '16 at 17:44
  • @MonkeyZeus After attempting to retrieve the result. Line: `$result = $stmt->fetchAll(PDO::FETCH_ASSOC);` – Ignacy Debicki Jan 07 '16 at 17:45
  • Try it after the call to `prepare()`; – MonkeyZeus Jan 07 '16 at 17:53
  • @MonkeyZeus Same return of [00000,null,null] – Ignacy Debicki Jan 07 '16 at 17:54
  • You can debug your query binding using a custom function, like [this](http://stackoverflow.com/a/1376838/4937064) – randrade86 Jan 07 '16 at 17:54
  • I am starting to think something funky is being provided by the `request_connection()` function. Can you change your code to perform the PDO connection directly in your code? – MonkeyZeus Jan 07 '16 at 17:55
  • Maybe you should provide your PHP and MySQL versions. Not sure if something quirky is going on with your installation. – MonkeyZeus Jan 07 '16 at 17:56
  • @RodolfoAndrade Thanks for the tip. I have tried implementing the function you gave me and it's not replacing the placeholder variables. I will try and explore this path further, as I have checked the parameter names are identical – Ignacy Debicki Jan 07 '16 at 18:02
  • @MonkeyZeus MySQL version : 5.1.73 , PHP version: 5.6.13 . As for request_connection(), see updated answer – Ignacy Debicki Jan 07 '16 at 18:07
  • Altthough mysql can handle alias and table name being the same you shoud avoid it – Mihai Jan 07 '16 at 18:17
  • @RodolfoAndrade I discovered the function also prepends a : and thats why it wasn't working. I fixed it in the function and the SQL statement that I get back works perfectly in phpMyAdmin. – Ignacy Debicki Jan 07 '16 at 18:18
  • This is incredibly odd, I am starting to think that you are walking a very thin line by using [reserved words](http://dev.mysql.com/doc/refman/5.7/en/keywords.html) as field names. I recommend putting backticks `\`` on all of your field names and aliases within the SQL. – MonkeyZeus Jan 07 '16 at 18:34
  • @MonkeyZeus I've just tried. Still no effect. I'll try rewriting the whole function. – Ignacy Debicki Jan 07 '16 at 18:40

1 Answers1

3

After many hours of trying, I have finally found my solution.

Two important statements that I had missed out from creating my connection are:

$conn->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

which turn on error reporting (See https://stackoverflow.com/a/8776392/2891273).

Once I turned this on, it was trivial to catch my problem, which was due to the overwriting of the :to parameter if $to was 0, the number of parameters passed in the $conn->execute() statement was mismatched with the number of parameters in the sql query.

My solution was to use $conn->bindValue() for each parameter instead, using an if statement to check if to bind to the :to parameter. Below is my solution:

function get_highscore_list($map,$limit,$from,$to){
    $sql='SELECT user, scoreVal AS score, UNIX_TIMESTAMP(timestamp) AS timestamp FROM Score WHERE map = :map AND timestamp >= :from AND timestamp <= :to ORDER BY scoreVal DESC, timestamp ASC LIMIT :limit';
    if ($to==0){
        $sql = str_replace(":to",'CURRENT_TIMESTAMP()',$sql);
    }
    $conn = request_connection();
    $stmt = $conn->prepare($sql);
    $stmt->bindValue(':map',$map,PDO::PARAM_INT);
    $stmt->bindValue(':from',$from,PDO::PARAM_INT);
    if ($to!=0){
        $stmt->bindValue(':to',$to,PDO::PARAM_INT);
    }
    $stmt->bindValue(':limit',$limit,PDO::PARAM_INT);
    $stmt->execute();
    $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
    return $result;
}
Community
  • 1
  • 1
Ignacy Debicki
  • 437
  • 4
  • 18
  • So, Doing this i found out "sql_mode=only_full_group_by" And Googles CloudSQL do not mix well. I can change it in my Client, but in my code, it still throws the error. Only found out thanks to those attributes. Goodbye Legacy code, Second Generation Cloud SQL is too strict for it. – Ryan Oct 05 '16 at 19:08