Skip to content

Commit f054eff

Browse files
committed
Add support for INDEX hints in SELECT statement
Signed-off-by: Deven Bansod <[email protected]>
1 parent 09faf92 commit f054eff

18 files changed

+277
-0
lines changed

src/Components/IndexHint.php

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
<?php
2+
3+
/**
4+
* Parses an Index hint.
5+
*/
6+
7+
namespace PhpMyAdmin\SqlParser\Components;
8+
9+
use PhpMyAdmin\SqlParser\Component;
10+
use PhpMyAdmin\SqlParser\Parser;
11+
use PhpMyAdmin\SqlParser\Token;
12+
use PhpMyAdmin\SqlParser\TokensList;
13+
14+
/**
15+
* Parses an Index hint.
16+
*
17+
* @category Components
18+
*
19+
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
20+
*/
21+
class IndexHint extends Component
22+
{
23+
/**
24+
* The type of hint (USE/FORCE/IGNORE)
25+
*
26+
* @var string
27+
*/
28+
public $type;
29+
30+
/**
31+
* What the hint is for (INDEX/KEY)
32+
*
33+
* @var string
34+
*/
35+
public $indexOrKey;
36+
37+
/**
38+
* The clause for which this hint is (JOIN/ORDER BY/GROUP BY)
39+
*
40+
* @var string
41+
*/
42+
public $for;
43+
44+
/**
45+
* List of indexes in this hint
46+
*
47+
* @var array
48+
*/
49+
public $indexes = array();
50+
51+
/**
52+
* Constructor.
53+
*
54+
* @param string $type the type of hint (USE/FORCE/IGNORE)
55+
* @param string $indexOrKey What the hint is for (INDEX/KEY)
56+
* @param string $for the clause for which this hint is (JOIN/ORDER BY/GROUP BY)
57+
* @param string $indexes List of indexes in this hint
58+
*/
59+
public function __construct(string $type = null, string $indexOrKey = null, string $for = null, array $indexes = array())
60+
{
61+
$this->type = $type;
62+
$this->indexOrKey = $indexOrKey;
63+
$this->for = $for;
64+
$this->indexes = $indexes;
65+
}
66+
67+
/**
68+
* @param Parser $parser the parser that serves as context
69+
* @param TokensList $list the list of tokens that are being parsed
70+
* @param array $options parameters for parsing
71+
*
72+
* @return IndexHint|Component[]
73+
*/
74+
public static function parse(Parser $parser, TokensList $list, array $options = array())
75+
{
76+
$ret = array();
77+
$expr = new self();
78+
$expr->type = isset($options['type']) ? $options['type'] : null;
79+
80+
/**
81+
* The state of the parser.
82+
*
83+
* Below are the states of the parser.
84+
*
85+
* 0 ----------------- [ USE/IGNORE/FORCE ]-----------------> 1
86+
* 1 -------------------- [ INDEX/KEY ] --------------------> 2
87+
* 2 ----------------------- [ FOR ] -----------------------> 3
88+
* 2 -------------------- [ expr_list ] --------------------> 0
89+
* 3 -------------- [ JOIN/GROUP BY/ORDER BY ] -------------> 4
90+
* 4 -------------------- [ expr_list ] --------------------> 0
91+
*
92+
* @var int
93+
*/
94+
$state = 0;
95+
96+
// By design, the parser will parse first token after the keyword.
97+
// In this case, the keyword must be analyzed too, in order to determine
98+
// the type of this hint.
99+
if ($list->idx > 0) {
100+
--$list->idx;
101+
}
102+
103+
for (; $list->idx < $list->count; ++$list->idx) {
104+
/**
105+
* Token parsed at this moment.
106+
*
107+
* @var Token
108+
*/
109+
$token = $list->tokens[$list->idx];
110+
111+
// End of statement.
112+
if ($token->type === Token::TYPE_DELIMITER) {
113+
break;
114+
}
115+
116+
// Skipping whitespaces and comments.
117+
if (($token->type === Token::TYPE_WHITESPACE)
118+
|| ($token->type === Token::TYPE_COMMENT)
119+
) {
120+
continue;
121+
}
122+
123+
switch ($state) {
124+
case 0:
125+
if ($token->type === Token::TYPE_KEYWORD) {
126+
switch ($token->keyword) {
127+
case 'USE':
128+
case 'IGNORE':
129+
case 'FORCE':
130+
$expr->type = $token->keyword;
131+
break;
132+
default:
133+
break 3;
134+
}
135+
$state = 1;
136+
}
137+
break;
138+
case 1:
139+
if ($token->type === Token::TYPE_KEYWORD) {
140+
switch ($token->keyword) {
141+
case 'INDEX':
142+
case 'KEY':
143+
$expr->indexOrKey = $token->keyword;
144+
break;
145+
default:
146+
$parser->error('Unexpected keyword.', $token);
147+
break;
148+
}
149+
$state = 2;
150+
} else {
151+
// we expect the token to be a keyword
152+
$parser->error('Unexpected token.', $token);
153+
}
154+
break;
155+
case 2:
156+
if ($token->type === Token::TYPE_KEYWORD && $token->keyword === 'FOR') {
157+
$state = 3;
158+
} else {
159+
$expr->indexes = ExpressionArray::parse($parser, $list);
160+
$state = 0;
161+
$ret[] = $expr;
162+
$expr = new self();
163+
}
164+
break;
165+
case 3:
166+
if ($token->type === Token::TYPE_KEYWORD) {
167+
switch ($token->keyword) {
168+
case 'JOIN':
169+
case 'GROUP BY':
170+
case 'ORDER BY':
171+
$expr->for = $token->keyword;
172+
break;
173+
default:
174+
$parser->error('Unexpected keyword.', $token);
175+
break;
176+
}
177+
$state = 4;
178+
} else {
179+
// we expect the token to be a keyword
180+
$parser->error('Unexpected token.', $token);
181+
}
182+
break;
183+
case 4:
184+
$expr->indexes = ExpressionArray::parse($parser, $list);
185+
$state = 0;
186+
$ret[] = $expr;
187+
$expr = new self();
188+
break;
189+
}
190+
}
191+
--$list->idx;
192+
193+
return $ret;
194+
}
195+
196+
/**
197+
* @param ArrayObj|ArrayObj[] $component the component to be built
198+
* @param array $options parameters for building
199+
*
200+
* @return string
201+
*/
202+
public static function build($component, array $options = array())
203+
{
204+
if (is_array($component)) {
205+
return implode(' ', $component);
206+
}
207+
208+
$ret = $component->type . ' ' . $component->indexOrKey . ' ';
209+
if ($component->for !== null) {
210+
$ret .= 'FOR ' . $component->for . ' ';
211+
}
212+
return $ret . ExpressionArray::build($component->indexes);
213+
}
214+
}

src/Parser.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,10 @@ class Parser extends Core
177177
'field' => 'fields',
178178
'options' => array('parseField' => 'table'),
179179
),
180+
'FORCE' => array(
181+
'class' => 'PhpMyAdmin\\SqlParser\\Components\\IndexHint',
182+
'field' => 'index_hints',
183+
),
180184
'FROM' => array(
181185
'class' => 'PhpMyAdmin\\SqlParser\\Components\\ExpressionArray',
182186
'field' => 'from',
@@ -190,6 +194,10 @@ class Parser extends Core
190194
'class' => 'PhpMyAdmin\\SqlParser\\Components\\Condition',
191195
'field' => 'having',
192196
),
197+
'IGNORE' => array(
198+
'class' => 'PhpMyAdmin\\SqlParser\\Components\\IndexHint',
199+
'field' => 'index_hints',
200+
),
193201
'INTO' => array(
194202
'class' => 'PhpMyAdmin\\SqlParser\\Components\\IntoKeyword',
195203
'field' => 'into',
@@ -308,6 +316,10 @@ class Parser extends Core
308316
'field' => 'tables',
309317
'options' => array('parseField' => 'table'),
310318
),
319+
'USE' => array(
320+
'class' => 'PhpMyAdmin\\SqlParser\\Components\\IndexHint',
321+
'field' => 'index_hints',
322+
),
311323
'VALUE' => array(
312324
'class' => 'PhpMyAdmin\\SqlParser\\Components\\Array2d',
313325
'field' => 'values',

src/Statement.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,17 @@ public function validateClauseOrder($parser, $list)
497497
$clauseType
498498
);
499499

500+
if ($clauseStartIdx !== -1
501+
&& $this instanceof Statements\SelectStatement
502+
&& ($clauseType === 'FORCE'
503+
|| $clauseType === 'IGNORE'
504+
|| $clauseType === 'USE')
505+
) {
506+
// TODO: ordering of clauses in a SELECT statement with
507+
// Index hints is not supported
508+
return true;
509+
}
510+
500511
// Handle ordering of Multiple Joins in a query
501512
if ($clauseStartIdx !== -1) {
502513
if ($minJoin === 0 && stripos($clauseType, 'JOIN')) {

src/Statements/SelectStatement.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ class SelectStatement extends Statement
9191
'_SELECT' => array('SELECT', 1),
9292
'INTO' => array('INTO', 3),
9393
'FROM' => array('FROM', 3),
94+
'FORCE' => array('FORCE', 1),
95+
'USE' => array('USE', 1),
96+
'IGNORE' => array('IGNORE', 3),
9497
'PARTITION' => array('PARTITION', 3),
9598

9699
'JOIN' => array('JOIN', 1),
@@ -135,6 +138,13 @@ class SelectStatement extends Statement
135138
*/
136139
public $from = array();
137140

141+
/**
142+
* Index hints
143+
*
144+
* @var IndexHint[]
145+
*/
146+
public $index_hints;
147+
138148
/**
139149
* Partitions used as source for this statement.
140150
*

tests/Builder/SelectStatementTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,16 @@ public function testBuildGroupBy()
102102
$stmt->build()
103103
);
104104
}
105+
106+
public function testBuildIndexHint()
107+
{
108+
$query = 'SELECT * FROM address FORCE INDEX (idx_fk_city_id) IGNORE KEY FOR GROUP BY (a, b,c) WHERE city_id<0 ';
109+
$parser = new Parser($query);
110+
$stmt = $parser->statements[0];
111+
112+
$this->assertEquals(
113+
$query,
114+
$stmt->build()
115+
);
116+
}
105117
}

tests/Parser/SelectStatementTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ public function testSelectProvider()
6969
array('parser/parseSelectEndOptionsErr'),
7070
array('parser/parseSelectUnion'),
7171
array('parser/parseSelectUnion2'),
72+
array('parser/parseSelectIndexHint1'),
73+
array('parser/parseSelectIndexHint2'),
74+
array('parser/parseSelectIndexHintErr1'),
75+
array('parser/parseSelectIndexHintErr2'),
76+
array('parser/parseSelectIndexHintErr3'),
77+
array('parser/parseSelectIndexHintErr4'),
7278
);
7379
}
7480
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SELECT * FROM address FORCE INDEX (idx_fk_city_id) IGNORE KEY FOR GROUP BY (a, b,c) WHERE city_id<0;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
a:4:{s:5:"query";s:101:"SELECT * FROM address FORCE INDEX (idx_fk_city_id) IGNORE KEY FOR GROUP BY (a, b,c) WHERE city_id<0;";s:5:"lexer";O:26:"PhpMyAdmin\SqlParser\Lexer":8:{s:3:"str";s:101:"SELECT * FROM address FORCE INDEX (idx_fk_city_id) IGNORE KEY FOR GROUP BY (a, b,c) WHERE city_id<0;";s:3:"len";i:101;s:4:"last";i:101;s:4:"list";O:31:"PhpMyAdmin\SqlParser\TokensList":3:{s:6:"tokens";a:40:{i:0;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:7:"keyword";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:3;s:8:"position";i:0;}i:1;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:2:" ";s:5:"value";s:1:" ";s:7:"keyword";N;s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:6;}i:2;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:"*";s:5:"value";s:1:"*";s:7:"keyword";N;s:4:"type";i:2;s:5:"flags";i:1;s:8:"position";i:8;}i:3;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:7:"keyword";N;s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:9;}i:4;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:4:"FROM";s:5:"value";s:4:"FROM";s:7:"keyword";s:4:"FROM";s:4:"type";i:1;s:5:"flags";i:3;s:8:"position";i:10;}i:5;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:7:"keyword";N;s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:14;}i:6;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:7:"address";s:5:"value";s:7:"address";s:7:"keyword";N;s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:15;}i:7;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:7:"keyword";N;s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:22;}i:8;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:5:"FORCE";s:5:"value";s:5:"FORCE";s:7:"keyword";s:5:"FORCE";s:4:"type";i:1;s:5:"flags";i:3;s:8:"position";i:23;}i:9;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:7:"keyword";N;s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:28;}i:10;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:5:"INDEX";s:5:"value";s:5:"INDEX";s:7:"keyword";s:5:"INDEX";s:4:"type";i:1;s:5:"flags";i:19;s:8:"position";i:29;}i:11;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:7:"keyword";N;s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:34;}i:12;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:7:"keyword";N;s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:35;}i:13;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:14:"idx_fk_city_id";s:5:"value";s:14:"idx_fk_city_id";s:7:"keyword";N;s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:36;}i:14;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:7:"keyword";N;s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:50;}i:15;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:7:"keyword";N;s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:51;}i:16;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:6:"IGNORE";s:5:"value";s:6:"IGNORE";s:7:"keyword";s:6:"IGNORE";s:4:"type";i:1;s:5:"flags";i:3;s:8:"position";i:52;}i:17;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:7:"keyword";N;s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:58;}i:18;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:3:"KEY";s:5:"value";s:3:"KEY";s:7:"keyword";s:3:"KEY";s:4:"type";i:1;s:5:"flags";i:19;s:8:"position";i:59;}i:19;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:7:"keyword";N;s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:62;}i:20;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:3:"FOR";s:5:"value";s:3:"FOR";s:7:"keyword";s:3:"FOR";s:4:"type";i:1;s:5:"flags";i:3;s:8:"position";i:63;}i:21;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:7:"keyword";N;s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:66;}i:22;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:8:"GROUP BY";s:5:"value";s:8:"GROUP BY";s:7:"keyword";s:8:"GROUP BY";s:4:"type";i:1;s:5:"flags";i:7;s:8:"position";i:67;}i:23;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:7:"keyword";N;s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:75;}i:24;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:7:"keyword";N;s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:76;}i:25;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:"a";s:5:"value";s:1:"a";s:7:"keyword";N;s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:77;}i:26;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:7:"keyword";N;s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:78;}i:27;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:7:"keyword";N;s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:79;}i:28;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:"b";s:5:"value";s:1:"b";s:7:"keyword";N;s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:80;}i:29;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:",";s:5:"value";s:1:",";s:7:"keyword";N;s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:81;}i:30;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:"c";s:5:"value";s:1:"c";s:7:"keyword";N;s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:82;}i:31;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:7:"keyword";N;s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:83;}i:32;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:7:"keyword";N;s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:84;}i:33;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:5:"WHERE";s:5:"value";s:5:"WHERE";s:7:"keyword";s:5:"WHERE";s:4:"type";i:1;s:5:"flags";i:3;s:8:"position";i:85;}i:34;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:7:"keyword";N;s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:90;}i:35;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:7:"city_id";s:5:"value";s:7:"city_id";s:7:"keyword";N;s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:91;}i:36;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:"<";s:5:"value";s:1:"<";s:7:"keyword";N;s:4:"type";i:2;s:5:"flags";i:2;s:8:"position";i:98;}i:37;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:"0";s:5:"value";i:0;s:7:"keyword";N;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:99;}i:38;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:7:"keyword";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:100;}i:39;O:26:"PhpMyAdmin\SqlParser\Token":6:{s:5:"token";N;s:5:"value";N;s:7:"keyword";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:40;s:3:"idx";i:40;}s:9:"delimiter";s:1:";";s:12:"delimiterLen";i:1;s:6:"strict";b:0;s:6:"errors";a:0:{}}s:6:"parser";O:27:"PhpMyAdmin\SqlParser\Parser":5:{s:4:"list";r:7;s:10:"statements";a:1:{i:0;O:47:"PhpMyAdmin\SqlParser\Statements\SelectStatement":17:{s:4:"expr";a:1:{i:0;O:42:"PhpMyAdmin\SqlParser\Components\Expression":7:{s:8:"database";N;s:5:"table";N;s:6:"column";N;s:4:"expr";s:1:"*";s:5:"alias";N;s:8:"function";N;s:8:"subquery";N;}}s:4:"from";a:1:{i:0;O:42:"PhpMyAdmin\SqlParser\Components\Expression":7:{s:8:"database";N;s:5:"table";s:7:"address";s:6:"column";N;s:4:"expr";s:7:"address";s:5:"alias";N;s:8:"function";N;s:8:"subquery";N;}}s:11:"index_hints";a:2:{i:0;O:41:"PhpMyAdmin\SqlParser\Components\IndexHint":4:{s:4:"type";s:5:"FORCE";s:10:"indexOrKey";s:5:"INDEX";s:3:"for";N;s:7:"indexes";a:1:{i:0;O:42:"PhpMyAdmin\SqlParser\Components\Expression":7:{s:8:"database";N;s:5:"table";N;s:6:"column";N;s:4:"expr";s:16:"(idx_fk_city_id)";s:5:"alias";N;s:8:"function";N;s:8:"subquery";N;}}}i:1;O:41:"PhpMyAdmin\SqlParser\Components\IndexHint":4:{s:4:"type";s:6:"IGNORE";s:10:"indexOrKey";s:3:"KEY";s:3:"for";s:8:"GROUP BY";s:7:"indexes";a:1:{i:0;O:42:"PhpMyAdmin\SqlParser\Components\Expression":7:{s:8:"database";N;s:5:"table";N;s:6:"column";N;s:4:"expr";s:8:"(a, b,c)";s:5:"alias";N;s:8:"function";N;s:8:"subquery";N;}}}}s:9:"partition";N;s:5:"where";a:1:{i:0;O:41:"PhpMyAdmin\SqlParser\Components\Condition":3:{s:11:"identifiers";a:1:{i:0;s:7:"city_id";}s:10:"isOperator";b:0;s:4:"expr";s:9:"city_id<0";}}s:5:"group";N;s:6:"having";N;s:5:"order";N;s:5:"limit";N;s:9:"procedure";N;s:4:"into";N;s:4:"join";N;s:5:"union";a:0:{}s:11:"end_options";N;s:7:"options";O:44:"PhpMyAdmin\SqlParser\Components\OptionsArray":1:{s:7:"options";a:0:{}}s:5:"first";i:0;s:4:"last";i:37;}}s:8:"brackets";i:0;s:6:"strict";b:0;s:6:"errors";a:0:{}}s:6:"errors";a:2:{s:5:"lexer";a:0:{}s:6:"parser";a:0:{}}}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SELECT * FROM address USE INDEX (idx_fk_city_id) FORCE KEY FOR GROUP BY (a, b,c) WHERE city_id<0

0 commit comments

Comments
 (0)