0

PDO does not rewind bound stream

I am using PHP PDO and MySQL.

I am trying to execute a ON DUPLICATE KEY UPDATE query, binding stream to two placeholders in the query.

Here is a code example:


<?php

/**
 * @param PDO $pdo
 * @param string $fileName
 * @param resource $fileContent
 */
function persistFromStream(PDO $pdo, string $fileName, $fileContent): void
{
    $query = <<<SQL
        INSERT INTO files (name, content)
        VALUES (:name, :content)
        ON DUPLICATE KEY UPDATE content = :contentSecondTime
        SQL;

    $statement = $pdo->prepare($query);

    $statement->bindParam(':name', $name);
    $statement->bindParam('content', $fileContent, PDO::PARAM_LOB);
    $statement->bindParam('contentSecondTime', $fileContent, PDO::PARAM_LOB);
    $statement->execute();
}

Insert works fine. "ON DUPLICATE" logic does not work fine: it writes empty string ('') into content column.

I suspect that this happens because PDO does not rewind my stream ($fileContent) when using it second time (in the "DUPLICATE clause").

If I copy the stream, both INSERT and ON DUPLICATE will work fine:

/**
 * @param PDO $pdo
 * @param string $fileName
 * @param resource $fileContent
 * @return void
 */
function persistFromStreamWithCopying(PDO $pdo, string $fileName, $fileContent): void
{
    $contentCopy = fopen('php://temp', 'r+');
    stream_copy_to_stream($fileContent, $contentCopy);
    rewind($fileContent);
    rewind($contentCopy);

    $query = <<<SQL
        INSERT INTO files (name, content)
        VALUES (:name, :content)
        ON DUPLICATE KEY UPDATE content = :contentSecondTime
        SQL;

    $statement = $pdo->prepare($query);

    $statement->bindParam(':name', $fileName);
    $statement->bindParam('content', $fileContent, PDO::PARAM_LOB);
    $statement->bindParam('contentSecondTime', $contentCopy, PDO::PARAM_LOB);
    $statement->execute();
}

The question is: Can I bind a single stream twice in a query, so PDO rewinds it somehow?

Thank you for your attention.

Kind regards.

Georgy Ivanov
  • 1,573
  • 1
  • 17
  • 24
  • You can only do this if you emulate prepares (see the 3rd and 4th answers to the dupe). Otherwise, just call 1 `payload1` and the other `payload2` and bind them separately – Nick Jun 14 '22 at 01:00
  • Hello, Nick. I have update the question according to your suggestion. It does not fix the issue; I think it's not about names of the parameters, it's about the stream not being rewind after being read for the first binding. – Georgy Ivanov Jun 14 '22 at 01:11
  • If that's the issue you should just be able to read it into another variable and bind to that instead. – Nick Jun 14 '22 at 01:22
  • Dear Nick, I know that I can read it into another variable and bind to that instead. The question is: can I execute the query without creating another variable? – Georgy Ivanov Jun 14 '22 at 01:28
  • 1
    Have you tried using `bindValue`? I've reopened the question since I agree it's not about the same thing – Nick Jun 14 '22 at 01:36
  • Thanks for the suggestion, but `bindValue` does not help. – Georgy Ivanov Jun 14 '22 at 01:48
  • 4
    I'd write this with `ON DUPLICATE KEY UPDATE content = VALUES(content)` without the `contentSecondTime` parameter. It'll just remember the value you tried to insert into that column and reuse it. Read about it here: https://dev.mysql.com/doc/refman/8.0/en/insert-on-duplicate.html – Bill Karwin Jun 14 '22 at 01:56
  • Hi, Bill. The solution with `VALUES(content)` works! Thank you. Care to create an answer? I will accept it. – Georgy Ivanov Jun 14 '22 at 07:30

0 Answers0