Skip to content

Commit 6bbaebf

Browse files
authored
Merge pull request #216 from devenbansod/fix/199
Add support for INDEX hints in SELECT statement
2 parents 60886eb + b422d7d commit 6bbaebf

18 files changed

+256
-0
lines changed

src/Components/IndexHint.php

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
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+
* The state of the parser.
81+
*
82+
* Below are the states of the parser.
83+
* 0 ----------------- [ USE/IGNORE/FORCE ]-----------------> 1
84+
* 1 -------------------- [ INDEX/KEY ] --------------------> 2
85+
* 2 ----------------------- [ FOR ] -----------------------> 3
86+
* 2 -------------------- [ expr_list ] --------------------> 0
87+
* 3 -------------- [ JOIN/GROUP BY/ORDER BY ] -------------> 4
88+
* 4 -------------------- [ expr_list ] --------------------> 0
89+
* @var int
90+
*/
91+
$state = 0;
92+
93+
// By design, the parser will parse first token after the keyword. So, the keyword
94+
// must be analyzed too, in order to determine the type of this index hint.
95+
if ($list->idx > 0) {
96+
--$list->idx;
97+
}
98+
for (; $list->idx < $list->count; ++$list->idx) {
99+
/**
100+
* Token parsed at this moment.
101+
*
102+
* @var Token
103+
*/
104+
$token = $list->tokens[$list->idx];
105+
106+
// End of statement.
107+
if ($token->type === Token::TYPE_DELIMITER) {
108+
break;
109+
}
110+
// Skipping whitespaces and comments.
111+
if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) {
112+
continue;
113+
}
114+
115+
switch ($state) {
116+
case 0:
117+
if ($token->type === Token::TYPE_KEYWORD) {
118+
if ($token->keyword === 'USE' || $token->keyword === 'IGNORE' || $token->keyword === 'FORCE') {
119+
$expr->type = $token->keyword;
120+
$state = 1;
121+
} else {
122+
break 2;
123+
}
124+
}
125+
break;
126+
case 1:
127+
if ($token->type === Token::TYPE_KEYWORD) {
128+
if ($token->keyword === 'INDEX' || $token->keyword === 'KEY') {
129+
$expr->indexOrKey = $token->keyword;
130+
} else {
131+
$parser->error('Unexpected keyword.', $token);
132+
}
133+
$state = 2;
134+
} else {
135+
// we expect the token to be a keyword
136+
$parser->error('Unexpected token.', $token);
137+
}
138+
break;
139+
case 2:
140+
if ($token->type === Token::TYPE_KEYWORD && $token->keyword === 'FOR') {
141+
$state = 3;
142+
} else {
143+
$expr->indexes = ExpressionArray::parse($parser, $list);
144+
$state = 0;
145+
$ret[] = $expr;
146+
$expr = new self();
147+
}
148+
break;
149+
case 3:
150+
if ($token->type === Token::TYPE_KEYWORD) {
151+
if ($token->keyword === 'JOIN' || $token->keyword === 'GROUP BY' || $token->keyword === 'ORDER BY') {
152+
$expr->for = $token->keyword;
153+
} else {
154+
$parser->error('Unexpected keyword.', $token);
155+
}
156+
$state = 4;
157+
} else {
158+
// we expect the token to be a keyword
159+
$parser->error('Unexpected token.', $token);
160+
}
161+
break;
162+
case 4:
163+
$expr->indexes = ExpressionArray::parse($parser, $list);
164+
$state = 0;
165+
$ret[] = $expr;
166+
$expr = new self();
167+
break;
168+
}
169+
}
170+
--$list->idx;
171+
172+
return $ret;
173+
}
174+
175+
/**
176+
* @param ArrayObj|ArrayObj[] $component the component to be built
177+
* @param array $options parameters for building
178+
*
179+
* @return string
180+
*/
181+
public static function build($component, array $options = array())
182+
{
183+
if (is_array($component)) {
184+
return implode(' ', $component);
185+
}
186+
187+
$ret = $component->type . ' ' . $component->indexOrKey . ' ';
188+
if ($component->for !== null) {
189+
$ret .= 'FOR ' . $component->for . ' ';
190+
}
191+
return $ret . ExpressionArray::build($component->indexes);
192+
}
193+
}

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)