Skip to content

Commit 6378ba0

Browse files
committed
Merge branch 'master' of https://github.com/dunhamjared/laravel-framework into dunhamjared-master
2 parents 103d38a + d89d2c7 commit 6378ba0

File tree

2 files changed

+34
-99
lines changed

2 files changed

+34
-99
lines changed

src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php

Lines changed: 31 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,25 @@ class SqlServerGrammar extends Grammar
1919
'&', '&=', '|', '|=', '^', '^=',
2020
];
2121

22+
/**
23+
* The components that make up a select clause.
24+
*
25+
* @var string[]
26+
*/
27+
protected $selectComponents = [
28+
'aggregate',
29+
'columns',
30+
'from',
31+
'joins',
32+
'wheres',
33+
'groups',
34+
'havings',
35+
'orders',
36+
'offset',
37+
'limit',
38+
'lock',
39+
];
40+
2241
/**
2342
* Compile a select query into SQL.
2443
*
@@ -27,26 +46,12 @@ class SqlServerGrammar extends Grammar
2746
*/
2847
public function compileSelect(Builder $query)
2948
{
30-
if (! $query->offset) {
31-
return parent::compileSelect($query);
32-
}
33-
34-
if (is_null($query->columns)) {
35-
$query->columns = ['*'];
49+
// An ORDER BY clause is required for offset to work
50+
if ($query->offset && empty($query->orders)) {
51+
$query->orders[] = ['sql' => '(SELECT 0)'];
3652
}
3753

38-
$components = $this->compileComponents($query);
39-
40-
if (! empty($components['orders'])) {
41-
return parent::compileSelect($query)." offset {$query->offset} rows fetch next {$query->limit} rows only";
42-
}
43-
44-
// If an offset is present on the query, we will need to wrap the query in
45-
// a big "ANSI" offset syntax block. This is very nasty compared to the
46-
// other database systems but is necessary for implementing features.
47-
return $this->compileAnsiOffset(
48-
$query, $components
49-
);
54+
return parent::compileSelect($query);
5055
}
5156

5257
/**
@@ -246,69 +251,6 @@ protected function compileHavingBitwise($having)
246251
return '('.$column.' '.$having['operator'].' '.$parameter.') != 0';
247252
}
248253

249-
/**
250-
* Create a full ANSI offset clause for the query.
251-
*
252-
* @param \Illuminate\Database\Query\Builder $query
253-
* @param array $components
254-
* @return string
255-
*/
256-
protected function compileAnsiOffset(Builder $query, $components)
257-
{
258-
// An ORDER BY clause is required to make this offset query work, so if one does
259-
// not exist we'll just create a dummy clause to trick the database and so it
260-
// does not complain about the queries for not having an "order by" clause.
261-
if (empty($components['orders'])) {
262-
$components['orders'] = 'order by (select 0)';
263-
}
264-
265-
// We need to add the row number to the query so we can compare it to the offset
266-
// and limit values given for the statements. So we will add an expression to
267-
// the "select" that will give back the row numbers on each of the records.
268-
$components['columns'] .= $this->compileOver($components['orders']);
269-
270-
unset($components['orders']);
271-
272-
if ($this->queryOrderContainsSubquery($query)) {
273-
$query->bindings = $this->sortBindingsForSubqueryOrderBy($query);
274-
}
275-
276-
// Next we need to calculate the constraints that should be placed on the query
277-
// to get the right offset and limit from our query but if there is no limit
278-
// set we will just handle the offset only since that is all that matters.
279-
$sql = $this->concatenate($components);
280-
281-
return $this->compileTableExpression($sql, $query);
282-
}
283-
284-
/**
285-
* Compile the over statement for a table expression.
286-
*
287-
* @param string $orderings
288-
* @return string
289-
*/
290-
protected function compileOver($orderings)
291-
{
292-
return ", row_number() over ({$orderings}) as row_num";
293-
}
294-
295-
/**
296-
* Determine if the query's order by clauses contain a subquery.
297-
*
298-
* @param \Illuminate\Database\Query\Builder $query
299-
* @return bool
300-
*/
301-
protected function queryOrderContainsSubquery($query)
302-
{
303-
if (! is_array($query->orders)) {
304-
return false;
305-
}
306-
307-
return Arr::first($query->orders, function ($value) {
308-
return $this->isExpression($value['column'] ?? null);
309-
}, false) !== false;
310-
}
311-
312254
/**
313255
* Move the order bindings to be after the "select" statement to account for an order by subquery.
314256
*
@@ -322,20 +264,6 @@ protected function sortBindingsForSubqueryOrderBy($query)
322264
});
323265
}
324266

325-
/**
326-
* Compile a common table expression for a query.
327-
*
328-
* @param string $sql
329-
* @param \Illuminate\Database\Query\Builder $query
330-
* @return string
331-
*/
332-
protected function compileTableExpression($sql, $query)
333-
{
334-
$constraint = $this->compileRowConstraint($query);
335-
336-
return "select * from ({$sql}) as temp_table where row_num {$constraint} order by row_num";
337-
}
338-
339267
/**
340268
* Compile the limit / offset row constraint for a query.
341269
*
@@ -392,6 +320,10 @@ public function compileRandom($seed)
392320
*/
393321
protected function compileLimit(Builder $query, $limit)
394322
{
323+
if ($limit && $query->offset > 0) {
324+
return "fetch next {$limit} rows only";
325+
}
326+
395327
return '';
396328
}
397329

@@ -404,6 +336,10 @@ protected function compileLimit(Builder $query, $limit)
404336
*/
405337
protected function compileOffset(Builder $query, $offset)
406338
{
339+
if ($offset) {
340+
return "offset {$offset} rows";
341+
}
342+
407343
return '';
408344
}
409345

tests/Database/DatabaseQueryBuilderTest.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3691,19 +3691,18 @@ public function testSqlServerLimitsAndOffsets()
36913691
$this->assertSame('select top 10 * from [users]', $builder->toSql());
36923692

36933693
$builder = $this->getSqlServerBuilder();
3694-
$builder->select('*')->from('users')->skip(10);
3695-
$this->assertSame('select * from (select *, row_number() over (order by (select 0)) as row_num from [users]) as temp_table where row_num >= 11 order by row_num', $builder->toSql());
3694+
$builder->select('*')->from('users')->skip(10)->orderBy('email', 'desc');
3695+
$this->assertSame('select * from [users] order by [email] desc offset 10 rows', $builder->toSql());
36963696

36973697
$builder = $this->getSqlServerBuilder();
36983698
$builder->select('*')->from('users')->skip(10)->take(10);
3699-
$this->assertSame('select * from (select *, row_number() over (order by (select 0)) as row_num from [users]) as temp_table where row_num between 11 and 20 order by row_num', $builder->toSql());
3699+
$this->assertSame('select * from [users] order by (SELECT 0) offset 10 rows fetch next 10 rows only', $builder->toSql());
37003700

37013701
$builder = $this->getSqlServerBuilder();
37023702
$builder->select('*')->from('users')->skip(11)->take(10)->orderBy('email', 'desc');
37033703
$this->assertSame('select * from [users] order by [email] desc offset 11 rows fetch next 10 rows only', $builder->toSql());
37043704

37053705
$builder = $this->getSqlServerBuilder();
3706-
$subQueryBuilder = $this->getSqlServerBuilder();
37073706
$subQuery = function ($query) {
37083707
return $query->select('created_at')->from('logins')->where('users.name', 'nameBinding')->whereColumn('user_id', 'users.id')->limit(1);
37093708
};

0 commit comments

Comments
 (0)