Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to insert resource (stream) into table for MySQL, or SQLite database #41180

Closed
aedart opened this issue Feb 22, 2022 · 4 comments
Closed

Comments

@aedart
Copy link
Contributor

aedart commented Feb 22, 2022

  • Laravel Version: 8.x | 9.x
  • PHP Version: 7.4.x | 8.x
  • Database Driver & Version: MySql, SQLite

Description:

When attempting to store a large file into a database table, e.g. a large photo, the database Connection does not bind a resource correctly, for MySQL and SQLite. Yet, for Postgresql it works?

Steps To Reproduce:

use Illuminate\Support\Facades\DB;

$file = fopen('/pictures/large_image.jpg', 'rb');

// Insert large file into table...
DB::table('files')
    ->insert([
        'file' => $file
    ]);

// Select inserted record (...not shown here...)

echo $record->file; // "Resource id #1801" - expected file's content,...

The above example works when using Postgres connection.

Cause

The \Illuminate\Database\Connection::bindValues() method does not handle values of the type resource. Yet, the \Illuminate\Database\PostgresConnection does.

Code from PostgresConnection

// Inside PostgresConnection::bindValues()
foreach ($bindings as $key => $value) {
    if (is_int($value)) {
        $pdoParam = PDO::PARAM_INT;
    } elseif (is_resource($value)) {    // --> handling of resource type
        $pdoParam = PDO::PARAM_LOB;
    } else {
        $pdoParam = PDO::PARAM_STR;
    }

    // ...remaining not shown ...
}

Possible Solution

Update the \Illuminate\Database\Connection::bindValues() to handle values of type resource.

Alternative Solution

Perhaps allow specifying a callback that allows developers to define how values should be bound to PDO statement, in Connection.

E.g.

// Inside Connection class

public function bindUsing($callback) {
    $this->bindCallback = $callback;
}

public function bindValues($statement, $bindings)
{
    // Use custom callback, if one is provided...
    if (isset($this->bindCallback)) {
        return $this->bindCallback($statement, $bindings);
    }
    
    // Default to current implementation
    foreach ($bindings as $key => $value) {
        $statement->bindValue(
            is_string($key) ? $key : $key + 1,
            $value,
            is_int($value) ? PDO::PARAM_INT : PDO::PARAM_STR
        );
    }
}

If this solution should be chosen, then developers are free to resolve their PDO bindings, however they see fit :)

@driesvints
Copy link
Member

I think we'd be open to also implement this for other engines. Can you send a PR? Thanks

@aedart
Copy link
Contributor Author

aedart commented Feb 24, 2022

Hi @driesvints

I can try. Could you please answer the following:

  • which proposed solution would you like?
  • For what version of Laravel, e.g. 8.x or just for 9.x?

@driesvints
Copy link
Member

Think the first one. For 9.x because 8.x is closed for new features.

@aedart
Copy link
Contributor Author

aedart commented Feb 24, 2022

Alright then - I'll get started as soon as time permits it.

taylorotwell added a commit that referenced this issue Feb 24, 2022
* Add support for binding resources (#41180)

* Remove duplicate code (#41180)

Binding of resource values is now handled in `Connection`.

* formatting

Co-authored-by: Taylor Otwell <taylor@laravel.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants