Skip to content

Commit

Permalink
Added options to ignore order by in deletes and updates (#89)
Browse files Browse the repository at this point in the history
* Added an option to ignore order by in deletes

* Enable test only when integration tests are executed

* Fix code styling

* Apply suggestions from code review

Co-authored-by: adityamish <[email protected]>

* Added clarification according to the review

---------

Co-authored-by: AdalbertMemSQL <[email protected]>
Co-authored-by: adityamish <[email protected]>
  • Loading branch information
3 people authored Jul 26, 2024
1 parent 051eb68 commit 1be74cf
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 1 deletion.
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ This repository contains the official SingleStoreDB Driver for Laravel. This dri
- [Usage](#usage)
- [Issues connecting to SingleStore Managed Service](#issues-connecting-to-singlestore-managed-service)
- [Persistent Connections (performance optimization)](#persistent-connections-performance-optimization)
- [Order by in delete and update](#order-by-in-delete-and-update)
- [PHP Versions before 8.1](#php-versions-before-81)
- [Migrations](#migrations)
- [Universal Storage Tables (Columnstore)](#universal-storage-tables-columnstore)
Expand Down Expand Up @@ -123,6 +124,42 @@ To enable this feature, simply update your options to include `PDO::ATTR_PERSIST
]) : [],
```

## `ORDER BY` Clause in `DELETE` and `UPDATE` Queries

SingleStore does not support the `ORDER BY` clause in the `DELETE` and `UPDATE` queries.
Issuing queries similar to the following will return an error.

```php
DB::table('test')->orderBy('id', 'asc')->update(['id' => 1, 'a' => 'b']);
DB::table('test')->orderBy('id', 'asc')->delete();
```

You can configure the driver to ignore `orderBy` in `delete()` and `update()` requests by enabling `ignore_order_by_in_deletes` and
`ignore_order_by_in_updates` in the connection configuration, respectively. For example:

```php
[
'default' => env('DB_CONNECTION', 'singlestore'),

'connections' => [
'singlestore' => [
'driver' => 'singlestore',
'ignore_order_by_in_deletes' => true,
'ignore_order_by_in_updates' => true,
...
],
]
]
```

Note that when `orderBy` is ignored, it may result in deletion/update of different rows if query contains LIMIT or OFFSET.

Example:
```php
DB::table('user')->orderBy('score', 'asc')->limit(5)->delete();
```
In the following query when ORDER BY is ignored - 5 random users will be deleted instead of 5 users with the lowest score.

## PHP Versions before 8.1

In PHP versions before 8.1, the flag `PDO::ATTR_EMULATE_PREPARES` results in a bug by which all attributes returned by MySQL (and
Expand Down
2 changes: 1 addition & 1 deletion src/Connect/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function getSchemaBuilder()
*/
protected function getDefaultQueryGrammar()
{
$grammar = new Query\Grammar;
$grammar = new Query\Grammar($this->getConfig('ignore_order_by_in_deletes'), $this->getConfig('ignore_order_by_in_updates'));
if (method_exists($grammar, 'setConnection')) {
$grammar->setConnection($this);
}
Expand Down
46 changes: 46 additions & 0 deletions src/Query/Grammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,23 @@

namespace SingleStore\Laravel\Query;

use Exception;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Query\Grammars\MySqlGrammar;
use Illuminate\Support\Facades\Log;

class Grammar extends MySqlGrammar
{
private $ignoreOrderByInDeletes;

private $ignoreOrderByInUpdates;

public function __construct($ignoreOrderByInDeletes, $ignoreOrderByInUpdates)
{
$this->ignoreOrderByInDeletes = $ignoreOrderByInDeletes;
$this->ignoreOrderByInUpdates = $ignoreOrderByInUpdates;
}

public function compileOptions(array $options): string
{
$optionString = '';
Expand All @@ -20,6 +32,40 @@ public function compileOptions(array $options): string
return "OPTION ({$optionString})";
}

public function compileDelete(Builder $query)
{
// TODO: allow order by in the case when table has unique column
if (isset($query->orders)) {
if ($this->ignoreOrderByInDeletes) {
if (env('APP_ENV') !== 'production') {
Log::warning('SingleStore does not support the "ORDER BY" clause in a "DELETE" statement. The "ORDER BY" clause will be ignored.');
}
$query->orders = [];
} else {
throw new Exception('SingleStore does not support the "ORDER BY" clause in a "DELETE" statement. Enable the "ignore_order_by_in_deletes" configuration to ignore "orderBy" in "delete" operations.');
}
}

return parent::compileDelete($query);
}

public function compileUpdate(Builder $query, array $values)
{
// TODO: allow order by in the case when table has unique column
if (isset($query->orders)) {
if ($this->ignoreOrderByInUpdates) {
if (env('APP_ENV') !== 'production') {
Log::warning('SingleStore does not support the "ORDER BY" clause in an "UPDATE" statement. The "ORDER BY" clause will be ignored.');
}
$query->orders = [];
} else {
throw new Exception('SingleStore does not support the "ORDER BY" clause in an update statement. Enable the "ignore_order_by_in_updates" configuration to ignore "orderBy" in "update" operations.');
}
}

return parent::compileUpdate($query, $values);
}

/**
* Compile a "where fulltext" clause.
*
Expand Down
2 changes: 2 additions & 0 deletions tests/BaseTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public function getEnvironmentSetUp($app)
$app['config']->set('database.default', 'mysql');
$app['config']->set('database.connections.mysql.driver', 'singlestore');
$app['config']->set('database.connections.mysql.options.'.PDO::ATTR_EMULATE_PREPARES, true);
$app['config']->set('database.connections.mysql.ignore_order_by_in_deletes', true);
$app['config']->set('database.connections.mysql.ignore_order_by_in_updates', true);
}

public function singlestoreVersion()
Expand Down
49 changes: 49 additions & 0 deletions tests/Hybrid/OrderByTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

namespace SingleStore\Laravel\Tests\Hybrid;

use Illuminate\Support\Facades\DB;
use SingleStore\Laravel\Schema\Blueprint;
use SingleStore\Laravel\Tests\BaseTest;

class orderByTest extends BaseTest
{
use HybridTestHelpers;

/** @test */
public function ignoresOrderByInDelete()
{
if (! $this->runHybridIntegrations()) {
return;
}

$this->createTable(function (Blueprint $table) {
$table->id();
});

DB::table('test')->insert([
['id' => 1],
]);

DB::table('test')->orderBy('id', 'asc')->delete();
}

/** @test */
public function ignoresOrderByInUpdate()
{
if (! $this->runHybridIntegrations()) {
return;
}

$this->createTable(function (Blueprint $table) {
$table->id();
$table->string('a');
});

DB::table('test')->insert([
['id' => 1, 'a' => 'a'],
]);

DB::table('test')->orderBy('id', 'asc')->update(['id' => 1, 'a' => 'b']);
}
}

0 comments on commit 1be74cf

Please sign in to comment.