Skip to content

Commit 1be74cf

Browse files
Added options to ignore order by in deletes and updates (#89)
* 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 <89900889+adityamish@users.noreply.github.com> * Added clarification according to the review --------- Co-authored-by: AdalbertMemSQL <AdalbertMemSQL@users.noreply.github.com> Co-authored-by: adityamish <89900889+adityamish@users.noreply.github.com>
1 parent 051eb68 commit 1be74cf

File tree

5 files changed

+135
-1
lines changed

5 files changed

+135
-1
lines changed

README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ This repository contains the official SingleStoreDB Driver for Laravel. This dri
1515
- [Usage](#usage)
1616
- [Issues connecting to SingleStore Managed Service](#issues-connecting-to-singlestore-managed-service)
1717
- [Persistent Connections (performance optimization)](#persistent-connections-performance-optimization)
18+
- [Order by in delete and update](#order-by-in-delete-and-update)
1819
- [PHP Versions before 8.1](#php-versions-before-81)
1920
- [Migrations](#migrations)
2021
- [Universal Storage Tables (Columnstore)](#universal-storage-tables-columnstore)
@@ -123,6 +124,42 @@ To enable this feature, simply update your options to include `PDO::ATTR_PERSIST
123124
]) : [],
124125
```
125126

127+
## `ORDER BY` Clause in `DELETE` and `UPDATE` Queries
128+
129+
SingleStore does not support the `ORDER BY` clause in the `DELETE` and `UPDATE` queries.
130+
Issuing queries similar to the following will return an error.
131+
132+
```php
133+
DB::table('test')->orderBy('id', 'asc')->update(['id' => 1, 'a' => 'b']);
134+
DB::table('test')->orderBy('id', 'asc')->delete();
135+
```
136+
137+
You can configure the driver to ignore `orderBy` in `delete()` and `update()` requests by enabling `ignore_order_by_in_deletes` and
138+
`ignore_order_by_in_updates` in the connection configuration, respectively. For example:
139+
140+
```php
141+
[
142+
'default' => env('DB_CONNECTION', 'singlestore'),
143+
144+
'connections' => [
145+
'singlestore' => [
146+
'driver' => 'singlestore',
147+
'ignore_order_by_in_deletes' => true,
148+
'ignore_order_by_in_updates' => true,
149+
...
150+
],
151+
]
152+
]
153+
```
154+
155+
Note that when `orderBy` is ignored, it may result in deletion/update of different rows if query contains LIMIT or OFFSET.
156+
157+
Example:
158+
```php
159+
DB::table('user')->orderBy('score', 'asc')->limit(5)->delete();
160+
```
161+
In the following query when ORDER BY is ignored - 5 random users will be deleted instead of 5 users with the lowest score.
162+
126163
## PHP Versions before 8.1
127164

128165
In PHP versions before 8.1, the flag `PDO::ATTR_EMULATE_PREPARES` results in a bug by which all attributes returned by MySQL (and

src/Connect/Connection.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public function getSchemaBuilder()
3232
*/
3333
protected function getDefaultQueryGrammar()
3434
{
35-
$grammar = new Query\Grammar;
35+
$grammar = new Query\Grammar($this->getConfig('ignore_order_by_in_deletes'), $this->getConfig('ignore_order_by_in_updates'));
3636
if (method_exists($grammar, 'setConnection')) {
3737
$grammar->setConnection($this);
3838
}

src/Query/Grammar.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,23 @@
22

33
namespace SingleStore\Laravel\Query;
44

5+
use Exception;
56
use Illuminate\Database\Query\Builder;
67
use Illuminate\Database\Query\Grammars\MySqlGrammar;
8+
use Illuminate\Support\Facades\Log;
79

810
class Grammar extends MySqlGrammar
911
{
12+
private $ignoreOrderByInDeletes;
13+
14+
private $ignoreOrderByInUpdates;
15+
16+
public function __construct($ignoreOrderByInDeletes, $ignoreOrderByInUpdates)
17+
{
18+
$this->ignoreOrderByInDeletes = $ignoreOrderByInDeletes;
19+
$this->ignoreOrderByInUpdates = $ignoreOrderByInUpdates;
20+
}
21+
1022
public function compileOptions(array $options): string
1123
{
1224
$optionString = '';
@@ -20,6 +32,40 @@ public function compileOptions(array $options): string
2032
return "OPTION ({$optionString})";
2133
}
2234

35+
public function compileDelete(Builder $query)
36+
{
37+
// TODO: allow order by in the case when table has unique column
38+
if (isset($query->orders)) {
39+
if ($this->ignoreOrderByInDeletes) {
40+
if (env('APP_ENV') !== 'production') {
41+
Log::warning('SingleStore does not support the "ORDER BY" clause in a "DELETE" statement. The "ORDER BY" clause will be ignored.');
42+
}
43+
$query->orders = [];
44+
} else {
45+
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.');
46+
}
47+
}
48+
49+
return parent::compileDelete($query);
50+
}
51+
52+
public function compileUpdate(Builder $query, array $values)
53+
{
54+
// TODO: allow order by in the case when table has unique column
55+
if (isset($query->orders)) {
56+
if ($this->ignoreOrderByInUpdates) {
57+
if (env('APP_ENV') !== 'production') {
58+
Log::warning('SingleStore does not support the "ORDER BY" clause in an "UPDATE" statement. The "ORDER BY" clause will be ignored.');
59+
}
60+
$query->orders = [];
61+
} else {
62+
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.');
63+
}
64+
}
65+
66+
return parent::compileUpdate($query, $values);
67+
}
68+
2369
/**
2470
* Compile a "where fulltext" clause.
2571
*

tests/BaseTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ public function getEnvironmentSetUp($app)
3535
$app['config']->set('database.default', 'mysql');
3636
$app['config']->set('database.connections.mysql.driver', 'singlestore');
3737
$app['config']->set('database.connections.mysql.options.'.PDO::ATTR_EMULATE_PREPARES, true);
38+
$app['config']->set('database.connections.mysql.ignore_order_by_in_deletes', true);
39+
$app['config']->set('database.connections.mysql.ignore_order_by_in_updates', true);
3840
}
3941

4042
public function singlestoreVersion()

tests/Hybrid/OrderByTest.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
namespace SingleStore\Laravel\Tests\Hybrid;
4+
5+
use Illuminate\Support\Facades\DB;
6+
use SingleStore\Laravel\Schema\Blueprint;
7+
use SingleStore\Laravel\Tests\BaseTest;
8+
9+
class orderByTest extends BaseTest
10+
{
11+
use HybridTestHelpers;
12+
13+
/** @test */
14+
public function ignoresOrderByInDelete()
15+
{
16+
if (! $this->runHybridIntegrations()) {
17+
return;
18+
}
19+
20+
$this->createTable(function (Blueprint $table) {
21+
$table->id();
22+
});
23+
24+
DB::table('test')->insert([
25+
['id' => 1],
26+
]);
27+
28+
DB::table('test')->orderBy('id', 'asc')->delete();
29+
}
30+
31+
/** @test */
32+
public function ignoresOrderByInUpdate()
33+
{
34+
if (! $this->runHybridIntegrations()) {
35+
return;
36+
}
37+
38+
$this->createTable(function (Blueprint $table) {
39+
$table->id();
40+
$table->string('a');
41+
});
42+
43+
DB::table('test')->insert([
44+
['id' => 1, 'a' => 'a'],
45+
]);
46+
47+
DB::table('test')->orderBy('id', 'asc')->update(['id' => 1, 'a' => 'b']);
48+
}
49+
}

0 commit comments

Comments
 (0)