Skip to content

Update with subqueries is not supported for all databases #57374

@enduity

Description

@enduity

Laravel Version

12.33.0

PHP Version

8.3.19

Database Driver & Version

SQL Server - pdo_sqlsrv v5.12.0

Description

The Eloquent update() method is meant to support subqueries. However, this only works for MySQL and MariaDB. For other databases, trying to use update() with subqueries results in an error while the query is being prepared:

Object of class Closure could not be converted to string
Click to expand detailed error info

Relevant part of stack trace

[stacktrace]
#0 /var/www/app/vendor/laravel/framework/src/Illuminate/Database/Connection.php(718): PDOStatement->bindValue()
#1 /var/www/app/vendor/laravel/framework/src/Illuminate/Database/Connection.php(593): Illuminate\\Database\\Connection->bindValues()
#2 /var/www/app/vendor/laravel/framework/src/Illuminate/Database/Connection.php(811): Illuminate\\Database\\Connection->Illuminate\\Database\\{closure}()
#3 /var/www/app/vendor/laravel/framework/src/Illuminate/Database/Connection.php(778): Illuminate\\Database\\Connection->runQueryCallback()
#4 /var/www/app/vendor/laravel/framework/src/Illuminate/Database/Connection.php(583): Illuminate\\Database\\Connection->run()
#5 /var/www/app/vendor/laravel/framework/src/Illuminate/Database/Connection.php(535): Illuminate\\Database\\Connection->affectingStatement()
#6 /var/www/app/vendor/laravel/framework/src/Illuminate/Database/Query/Builder.php(3917): Illuminate\\Database\\Connection->update()

Full error message

[2025-10-13 16:25:47] local.ERROR: Object of class Closure could not be converted to string {"exception":"[object] (Error(code: 0): Object of class Closure could not be converted to string at /var/www/app/vendor/laravel/framework/src/Illuminate/Database/Connection.php:718)

Pretty error message

Note: Codebase-specific info was pixelated

Image

Code details

This functionality was added in PR #53254.

The bug is introduced within the same PR, in commit 7b2d56b. For unexplained reasons, in case of subquery update values, the $bindings values are wrapped into a closure. The method Grammar::prepareBindingsForUpdate() is updated to execute closure values, binding the return value to the query instead.

However, most overrides of Grammar::prepareBindingsForUpdate() don't use the original at all, and they were also not directly modified to support closures:

Steps To Reproduce

  1. Configure Laravel to use a database driver other than MySQL/MariaDB
    • Note: I have personally only verified this bug against SQL Server
  2. Run php artisan tinker in a shell
  3. Input and run this script:
    DB::table('fake_table')
        ->update([
            'fake_column' => DB::table('fake_source_table')->selectRaw('fake_source_column'),
        ]);
  4. Observe the error
     Error  Object of class Closure could not be converted to string.
    

References

Original issue, closed without resolution:

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions