tursodatabase / turso-driver-laravel

Turso Driver for Laravel with Native libSQL
https://turso.tech/sdk/php/guides/laravel
MIT License
51 stars 5 forks source link

[Bug]: BLOB columns are not working. #14

Closed ineersa closed 1 month ago

ineersa commented 1 month ago

What happened?

I'm trying to use TEXT column with libsql to store data, but it's not working currently.

Problem lies in how query bindings are handled - https://github.com/tursodatabase/turso-driver-laravel/blob/main/src/Database/LibSQLPDOStatement.php#L64

Here array_column($this->bindings, 'value') column value is passed, but if we dump actual bindings:

"type" => "blob"
"base64" => "VFhrZ1lt...."

We can see that for BLOB columns we receive this, and there is NO value column for blob. This result in empty blob column after insert.

If we change behavior to:

$bindings = [];

            foreach ($this->bindings as $key => $param) {
                if ($param['type'] === 'blob') {
                    $bindings[] = $param['base64'];
                } else {
                    $bindings[] = $param['value'];
                }
            }

            $this->response = $statement->query($bindings)->fetchArray(LibSQL::LIBSQL_ALL);

This will put base64 string inside BLOB column, which is not what we want.

If we will try to do base64_decode($param['base64']) and pass it further, we will get false as a result of operation, row will not be added, and we get 0 errors why this is happening.

Connection and statement classes are not compatible with PDO, on which internally all DB drivers and Laravel itself relies, so need to make them compatible with PDO classes.

How to reproduce the bug

Create a table with text columns and try to insert something there, see value of a column

        // SETUP libsql connection 
        $connection = \DB::connection('libsql');
        \Illuminate\Support\Facades\Schema::connection('libsql')
            ->dropIfExists('tests');
        \Illuminate\Support\Facades\Schema::connection('libsql')
            ->create('tests', function (Blueprint $table) {
                $table->increments('id');
                $table->text('content');
                $table->json('json');
                $table->timestamps();
            });

        $modelClass = new class extends \Illuminate\Database\Eloquent\Model {
            protected $connection = 'test_sqlite';
            protected $table = 'tests';
            public function casts()
            {
                return [
                    'json' => 'array',
                ];
            }
        };

        $model = new $modelClass();
        $model->content = "TEST CONTENT!";
        $model->json = ['test' => 'test'];
        $model->save();
        $model->refresh();
        dd($model);

Package Version

latest

PHP Version

8.3.8

Laravel Version

11.0

Which operating systems does with happen with?

Linux

Notes

No response

darkterminal commented 1 month ago

I am bit lost in this issue, tbh.

Here my implementation using base64 image (BLOB) in the current main branch version:

Here the table migration:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('images', function (Blueprint $table) {
            $table->id();
            $table->text('image');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('images');
    }
};

Here the model:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Image extends Model
{
    use HasFactory;
}

Here is the testing routes:

Route::get('/save-image', function() {
    $image = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAApgAAAKYB3X3/OAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANCSURBVEiJtZZPbBtFFMZ/M7ubXdtdb1xSFyeilBapySVU8h8OoFaooFSqiihIVIpQBKci6KEg9Q6H9kovIHoCIVQJJCKE1ENFjnAgcaSGC6rEnxBwA04Tx43t2FnvDAfjkNibxgHxnWb2e/u992bee7tCa00YFsffekFY+nUzFtjW0LrvjRXrCDIAaPLlW0nHL0SsZtVoaF98mLrx3pdhOqLtYPHChahZcYYO7KvPFxvRl5XPp1sN3adWiD1ZAqD6XYK1b/dvE5IWryTt2udLFedwc1+9kLp+vbbpoDh+6TklxBeAi9TL0taeWpdmZzQDry0AcO+jQ12RyohqqoYoo8RDwJrU+qXkjWtfi8Xxt58BdQuwQs9qC/afLwCw8tnQbqYAPsgxE1S6F3EAIXux2oQFKm0ihMsOF71dHYx+f3NND68ghCu1YIoePPQN1pGRABkJ6Bus96CutRZMydTl+TvuiRW1m3n0eDl0vRPcEysqdXn+jsQPsrHMquGeXEaY4Yk4wxWcY5V/9scqOMOVUFthatyTy8QyqwZ+kDURKoMWxNKr2EeqVKcTNOajqKoBgOE28U4tdQl5p5bwCw7BWquaZSzAPlwjlithJtp3pTImSqQRrb2Z8PHGigD4RZuNX6JYj6wj7O4TFLbCO/Mn/m8R+h6rYSUb3ekokRY6f/YukArN979jcW+V/S8g0eT/N3VN3kTqWbQ428m9/8k0P/1aIhF36PccEl6EhOcAUCrXKZXXWS3XKd2vc/TRBG9O5ELC17MmWubD2nKhUKZa26Ba2+D3P+4/MNCFwg59oWVeYhkzgN/JDR8deKBoD7Y+ljEjGZ0sosXVTvbc6RHirr2reNy1OXd6pJsQ+gqjk8VWFYmHrwBzW/n+uMPFiRwHB2I7ih8ciHFxIkd/3Omk5tCDV1t+2nNu5sxxpDFNx+huNhVT3/zMDz8usXC3ddaHBj1GHj/As08fwTS7Kt1HBTmyN29vdwAw+/wbwLVOJ3uAD1wi/dUH7Qei66PfyuRj4Ik9is+hglfbkbfR3cnZm7chlUWLdwmprtCohX4HUtlOcQjLYCu+fzGJH2QRKvP3UNz8bWk1qMxjGTOMThZ3kvgLI5AzFfo379UAAAAASUVORK5CYII=";

    $model = new Image();
    $model->image = $image;
    $model->save();
    echo "Image saved!";
});

Route::get('/display-image', function() {
    $img = Image::first();
    echo "<img src='data:image/png;base64,{$img->image}'>";
});
ineersa commented 1 month ago

@darkterminal https://github.com/tursodatabase/turso-driver-laravel/pull/15/files#diff-1845c220ace1bd4e6411e3abb2eb195ad1be46179ce58dffb8b416100ced8da6R83

Added test in PR, it's working on main since you rewrote how bindings were processed with PDO internally