Skip to content

Commit

Permalink
[9.x] Add index hinting support to query builder (#46063)
Browse files Browse the repository at this point in the history
* Add useIndex(), forceIndex(), ignoreIndex() methods to query builder

* formatting

---------

Co-authored-by: Taylor Otwell <taylor@laravel.com>
  • Loading branch information
Chris White and taylorotwell authored Feb 10, 2023
1 parent df863c0 commit 8a9123a
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 0 deletions.
46 changes: 46 additions & 0 deletions src/Illuminate/Database/Query/Builder.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@ class Builder implements BuilderContract
*/
public $from;

/**
* The index hint for the query.
*
* @var \Illuminate\Database\Query\IndexHint
*/
public $indexHint;

/**
* The table joins for the query.
*
Expand Down Expand Up @@ -458,6 +465,45 @@ public function from($table, $as = null)
return $this;
}

/**
* Add an index hint to suggest a query index.
*
* @param string $index
* @return $this
*/
public function useIndex($index)
{
$this->indexHint = new IndexHint('hint', $index);

return $this;
}

/**
* Add an index hint to force a query index.
*
* @param string $index
* @return $this
*/
public function forceIndex($index)
{
$this->indexHint = new IndexHint('force', $index);

return $this;
}

/**
* Add an index hint to ignore a query index.
*
* @param string $index
* @return $this
*/
public function ignoreIndex($index)
{
$this->indexHint = new IndexHint('ignore', $index);

return $this;
}

/**
* Add a join clause to the query.
*
Expand Down
1 change: 1 addition & 0 deletions src/Illuminate/Database/Query/Grammars/Grammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class Grammar extends BaseGrammar
'aggregate',
'columns',
'from',
'indexHint',
'joins',
'wheres',
'groups',
Expand Down
17 changes: 17 additions & 0 deletions src/Illuminate/Database/Query/Grammars/MySqlGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Illuminate\Database\Query\Grammars;

use Illuminate\Database\Query\Builder;
use Illuminate\Database\Query\IndexHint;
use Illuminate\Support\Str;

class MySqlGrammar extends Grammar
Expand Down Expand Up @@ -74,6 +75,22 @@ public function whereFullText(Builder $query, $where)
return "match ({$columns}) against (".$value."{$mode}{$expanded})";
}

/**
* Compile the index hints for the query.
*
* @param \Illuminate\Database\Query\Builder $query
* @param \Illuminate\Database\Query\IndexHint $indexHint
* @return string
*/
protected function compileIndexHint(Builder $query, $indexHint)
{
return match ($indexHint->type) {
'hint' => "use index ({$indexHint->index})",
'force' => "force index ({$indexHint->index})",
default => "ignore index ({$indexHint->index})",
};
}

/**
* Compile an insert ignore statement into SQL.
*
Expand Down
15 changes: 15 additions & 0 deletions src/Illuminate/Database/Query/Grammars/SQLiteGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Illuminate\Database\Query\Grammars;

use Illuminate\Database\Query\Builder;
use Illuminate\Database\Query\IndexHint;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;

Expand Down Expand Up @@ -117,6 +118,20 @@ protected function dateBasedWhere($type, Builder $query, $where)
return "strftime('{$type}', {$this->wrap($where['column'])}) {$where['operator']} cast({$value} as text)";
}

/**
* Compile the index hints for the query.
*
* @param \Illuminate\Database\Query\Builder $query
* @param \Illuminate\Database\Query\IndexHint $indexHint
* @return string
*/
protected function compileIndexHint(Builder $query, $indexHint)
{
return $indexHint->type === 'force'
? "indexed by {$indexHint->index}"
: '';
}

/**
* Compile a "JSON length" statement into SQL.
*
Expand Down
15 changes: 15 additions & 0 deletions src/Illuminate/Database/Query/Grammars/SqlServerGrammar.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Illuminate\Database\Query\Grammars;

use Illuminate\Database\Query\Builder;
use Illuminate\Database\Query\IndexHint;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;

Expand Down Expand Up @@ -96,6 +97,20 @@ protected function compileFrom(Builder $query, $table)
return $from;
}

/**
* Compile the index hints for the query.
*
* @param \Illuminate\Database\Query\Builder $query
* @param \Illuminate\Database\Query\IndexHint $indexHint
* @return string
*/
protected function compileIndexHint(Builder $query, $indexHint)
{
return $indexHint->type === 'force'
? "with (index({$indexHint->index}))"
: '';
}

/**
* {@inheritdoc}
*
Expand Down
33 changes: 33 additions & 0 deletions src/Illuminate/Database/Query/IndexHint.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace Illuminate\Database\Query;

class IndexHint
{
/**
* The type of query hint.
*
* @var string
*/
public $type;

/**
* The name of the index.
*
* @var string
*/
public $index;

/**
* Create a new index hint instance.
*
* @param string $type
* @param string $index
* @return void
*/
public function __construct($type, $index)
{
$this->type = $type;
$this->index = $index;
}
}
63 changes: 63 additions & 0 deletions tests/Database/DatabaseQueryBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5418,6 +5418,69 @@ public function testFromQuestionMarkOperatorOnPostgres()
$this->assertSame('select * from "users" where "roles" ??& ?', $builder->toSql());
}

public function testUseIndexMySql()
{
$builder = $this->getMySqlBuilder();
$builder->select('foo')->from('users')->useIndex('test_index');
$this->assertSame('select `foo` from `users` use index (test_index)', $builder->toSql());
}

public function testForceIndexMySql()
{
$builder = $this->getMySqlBuilder();
$builder->select('foo')->from('users')->forceIndex('test_index');
$this->assertSame('select `foo` from `users` force index (test_index)', $builder->toSql());
}

public function testIgnoreIndexMySql()
{
$builder = $this->getMySqlBuilder();
$builder->select('foo')->from('users')->ignoreIndex('test_index');
$this->assertSame('select `foo` from `users` ignore index (test_index)', $builder->toSql());
}

public function testUseIndexSqlite()
{
$builder = $this->getSQLiteBuilder();
$builder->select('foo')->from('users')->useIndex('test_index');
$this->assertSame('select "foo" from "users"', $builder->toSql());
}

public function testForceIndexSqlite()
{
$builder = $this->getSQLiteBuilder();
$builder->select('foo')->from('users')->forceIndex('test_index');
$this->assertSame('select "foo" from "users" indexed by test_index', $builder->toSql());
}

public function testIgnoreIndexSqlite()
{
$builder = $this->getSQLiteBuilder();
$builder->select('foo')->from('users')->ignoreIndex('test_index');
$this->assertSame('select "foo" from "users"', $builder->toSql());
}

public function testUseIndexSqlServer()
{
$builder = $this->getSqlServerBuilder();
$builder->select('foo')->from('users')->useIndex('test_index');
$this->assertSame('select [foo] from [users]', $builder->toSql());
}

public function testForceIndexSqlServer()
{
$builder = $this->getSqlServerBuilder();
$builder->select('foo')->from('users')->forceIndex('test_index');
$this->assertSame('select [foo] from [users] with (index(test_index))', $builder->toSql());
}

public function testIgnoreIndexSqlServer()
{
$builder = $this->getSqlServerBuilder();
$builder->select('foo')->from('users')->ignoreIndex('test_index');
$this->assertSame('select [foo] from [users]', $builder->toSql());
}

public function testClone()
{
$builder = $this->getBuilder();
Expand Down

0 comments on commit 8a9123a

Please sign in to comment.