laravel/framework · error · RuntimeException

This database engine does not support upserts.

Error message

This database engine does not support upserts.

What it means

The base Grammar's compileUpsert throws by default. All four major engine grammars (MySqlGrammar, PostgresGrammar, SQLiteGrammar, SqlServerGrammar) override it with engine-specific syntax (INSERT ... ON DUPLICATE KEY UPDATE / ON CONFLICT DO UPDATE / INSERT ON CONFLICT DO UPDATE / MERGE). This error fires only for a custom grammar extending the base Grammar without an override.

Solutions

  1. Switch to a built-in connection (mysql, pgsql, sqlite, or sqlsrv) — all four override compileUpsert
  2. Override compileUpsert in your custom grammar with the correct SQL for your engine
  3. Replace upsert() with a try/catch around insert() followed by update(), or use a raw statement with your engine's native upsert syntax

Example fix

// before — custom grammar without upsert support
DB::table('users')->upsert(['email' => 'a@b.com', 'name' => 'Alice'], ['email'], ['name']);

// after — manual insert-or-update fallback
$exists = DB::table('users')->where('email', 'a@b.com')->exists();
if ($exists) {
    DB::table('users')->where('email', 'a@b.com')->update(['name' => 'Alice']);
} else {
    DB::table('users')->insert(['email' => 'a@b.com', 'name' => 'Alice']);
}
Defensive patterns

Strategy: try-catch

Validate before calling

$grammar = DB::connection()->getQueryGrammar();
$method = new ReflectionMethod($grammar, 'compileUpsert');

if ($method->getDeclaringClass()->getName() !== 'Illuminate\\Database\\Query\\Grammars\\Grammar') {
    DB::table('users')->upsert($values, ['email'], ['name']);
}

Type guard

function grammarSupportsUpsert(): bool {
    $grammar = DB::connection()->getQueryGrammar();
    $method = new ReflectionMethod($grammar, 'compileUpsert');
    return $method->getDeclaringClass()->getName() !== 'Illuminate\\Database\\Query\\Grammars\\Grammar';
}

Try / catch

try {
    DB::table('users')->upsert($values, ['email'], ['name']);
} catch (\RuntimeException $e) {
    if (str_contains($e->getMessage(), 'upserts')) {
        // manual fallback: check existence then insert or update
        foreach ($values as $row) {
            $exists = DB::table('users')->where('email', $row['email'])->exists();
            $exists
                ? DB::table('users')->where('email', $row['email'])->update($row)
                : DB::table('users')->insert($row);
        }
    } else {
        throw $e;
    }
}

Prevention

When it happens

Trigger: Using upsert() on a query builder whose grammar is a custom class extending Grammar without implementing compileUpsert. This occurs with third-party or community database drivers that lack upsert support.

Common situations: Using a community database package (e.g., a ClickHouse, DynamoDB, or Cassandra driver) that extends the base Grammar but has not implemented upsert operations.

Related errors


AI-assisted analysis of laravel/framework@e0f6eb3518 (2026-08-11). Data as JSON: /api/errors/74068b23795d943e. Report an issue: GitHub.

Appendix: source

Thrown at src/Illuminate/Database/Query/Grammars/Grammar.php:1420

        $joins = $this->compileJoins($query, $query->joins);

        return "update {$table} {$joins} set {$columns} {$where}";
    }

    /**
     * Compile an "upsert" statement into SQL.
     *
     * @param  \Illuminate\Database\Query\Builder  $query
     * @param  array  $values
     * @param  array  $uniqueBy
     * @param  array  $update
     * @return string
     *
     * @throws \RuntimeException
     */
    public function compileUpsert(Builder $query, array $values, array $uniqueBy, array $update)
    {
        throw new RuntimeException('This database engine does not support upserts.');
    }

    /**
     * Prepare the bindings for an update statement.
     *
     * @param  array  $bindings
     * @param  array  $values
     * @return array
     */
    public function prepareBindingsForUpdate(array $bindings, array $values)
    {
        $cleanBindings = Arr::except($bindings, ['select', 'join']);

        $values = Arr::flatten(array_map(fn ($value) => value($value), $values));

        return array_values(
            array_merge($bindings['join'], $values, Arr::flatten($cleanBindings))
        );

View on GitHub (pinned to e0f6eb3518)