Skip to content
This repository was archived by the owner on Jan 4, 2022. It is now read-only.

Commit f0c357e

Browse files
committed
Implemented LIMIT clause support for Athena
1 parent 821504a commit f0c357e

File tree

2 files changed

+36
-6
lines changed

2 files changed

+36
-6
lines changed

src/Bilaliqbalr/Athena/src/Connection.php

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Illuminate\Database\QueryException;
1111
use Bilaliqbalr\Athena\Query\Grammar as QueryGrammar;
1212
use Bilaliqbalr\Athena\Query\Processor;
13+
use Illuminate\Support\Arr;
1314
use Illuminate\Support\Facades\Config;
1415
use Bilaliqbalr\Athena\Schema\Builder;
1516
use Bilaliqbalr\Athena\Schema\Grammar as SchemaGrammar;
@@ -181,11 +182,11 @@ public function formatCSVFileQueryResults($filePath)
181182
}
182183

183184
/**
184-
* @param \Illuminate\Database\Query\Builder $builder
185185
* @param null $query
186186
* @param null $binding
187187
*
188188
* @return mixed
189+
* @throws Exception
189190
*/
190191
protected function prepareQuery($query, $binding)
191192
{
@@ -197,6 +198,29 @@ protected function prepareQuery($query, $binding)
197198
}
198199
}
199200

201+
// Modifying query & preparing it for LIMIT as per Athena
202+
if (is_int(stripos($query, 'BETWEENLIMIT'))) {
203+
// Checking if ROW_NUMBER() OVER() window function applied, then take it as LIMIT query
204+
if (!is_int(stripos($query, 'ROW_NUMBER()')) || !is_int(stripos($query, ' rn '))) {
205+
throw new Exception("Error: Required `ROW_NUMBER() OVER(...) as rn` to implement LIMIT functionality");
206+
} else {
207+
$queryParts = preg_split("/BETWEENLIMIT/i", $query);
208+
preg_match_all('!\d+!', array_pop($queryParts), $matches);
209+
// Calculating offset and limit for Athena
210+
$perPage = ($matches[0][0] - 1);
211+
212+
// Only apply this limit if we have per page greater than 0.
213+
// This prevent BETWEEN as WHERE clause to be treated like LIMIT & OFFSET, which occurs if we have
214+
// both BETWEEN and ROW_NUMBER() in query but that no LIMIT
215+
if ($perPage > 0) {
216+
$page = ($matches[0][1] / $perPage) + 1;
217+
$from = ($perPage * ($page - 1)) + 1;
218+
$to = ($perPage * $page);
219+
$query = "SELECT * FROM ( " . Arr::first($queryParts) . " ) WHERE rn BETWEEN $from AND $to";
220+
}
221+
}
222+
}
223+
200224
return str_replace('`', '', $query);
201225
}
202226

@@ -216,7 +240,7 @@ public function getDownloadedFilePath()
216240
* @return array|\Aws\Result
217241
* @throws Exception
218242
*/
219-
protected function executeQuery($query, $bindings)
243+
protected function executeQuery(&$query, $bindings)
220244
{
221245
$query = $this->prepareQuery($query, $bindings);
222246

@@ -275,7 +299,7 @@ public function statement($query, $bindings = [])
275299
$this->executeQuery($query, $bindings);
276300

277301
$this->logQuery(
278-
$query, $bindings, $this->getElapsedTime($start)
302+
$query, [], $this->getElapsedTime($start)
279303
);
280304

281305
return true;
@@ -306,12 +330,11 @@ public function select($query, $bindings = [], $useReadPdo = true)
306330

307331
if ($this->downloadFileFromS3ToLocalServer($s3FilePath, $localFilePath)) {
308332
$this->localFilePath = $localFilePath;
309-
dd($s3FilePath, $this->localFilePath);
310333
$result = $this->formatCSVFileQueryResults($this->localFilePath);
311334
}
312335
}
313336
$this->logQuery(
314-
$query, $bindings, $this->getElapsedTime($start)
337+
$query, [], $this->getElapsedTime($start)
315338
);
316339

317340
return $result;

src/Bilaliqbalr/Athena/src/Query/Grammar.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,14 @@ class Grammar extends MySqlGrammar
1515
*/
1616
protected function compileLimit(Builder $query, $limit)
1717
{
18-
return 'BETWEEN '.(int) $limit;
18+
// Only apply BETWEEN clause, if missing OFFSET, otherwise use presto way to LIMIT records
19+
if (is_int($query->offset)) {
20+
// using custom BETWEENLIMIT clause only to detect if it is limit to prevent conflict with BETWEEN.
21+
// Handling it in Connection.php
22+
return 'BETWEENLIMIT '.(int) $limit;
23+
} else {
24+
return parent::compileLimit($query, $limit);
25+
}
1926
}
2027

2128
/**

0 commit comments

Comments
 (0)