Skip to content

Commit 48d61a0

Browse files
committed
Add GroupKeyword class to fix postgres GROUP BY
1 parent 286e464 commit 48d61a0

File tree

3 files changed

+166
-1
lines changed

3 files changed

+166
-1
lines changed

src/Components/GroupKeyword.php

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
<?php
2+
3+
/**
4+
* `GROUP BY` keyword parser.
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+
* `GROUP BY` keyword parser.
16+
*
17+
* @category Keywords
18+
*
19+
* @license https://www.gnu.org/licenses/gpl-2.0.txt GPL-2.0+
20+
*/
21+
class GroupKeyword extends Component
22+
{
23+
/**
24+
* The expression that is used for ordering.
25+
*
26+
* @var Expression
27+
*/
28+
public $expr;
29+
30+
/**
31+
* The order type.
32+
*
33+
* @var string
34+
*/
35+
public $type;
36+
37+
/**
38+
* Constructor.
39+
*
40+
* @param Expression $expr the expression that we are sorting by
41+
* @param string $type the sorting type
42+
*/
43+
public function __construct($expr = null, $type = '')
44+
{
45+
$this->expr = $expr;
46+
$this->type = $type;
47+
}
48+
49+
/**
50+
* @param Parser $parser the parser that serves as context
51+
* @param TokensList $list the list of tokens that are being parsed
52+
* @param array $options parameters for parsing
53+
*
54+
* @return GroupKeyword[]
55+
*/
56+
public static function parse(Parser $parser, TokensList $list, array $options = array())
57+
{
58+
$ret = array();
59+
60+
$expr = new self();
61+
62+
/**
63+
* The state of the parser.
64+
*
65+
* Below are the states of the parser.
66+
*
67+
* 0 --------------------[ expression ]-------------------> 1
68+
*
69+
* 1 ------------------------[ , ]------------------------> 0
70+
* 1 -------------------[ ASC / DESC ]--------------------> 1
71+
*
72+
* @var int
73+
*/
74+
$state = 0;
75+
76+
for (; $list->idx < $list->count; ++$list->idx) {
77+
/**
78+
* Token parsed at this moment.
79+
*
80+
* @var Token
81+
*/
82+
$token = $list->tokens[$list->idx];
83+
84+
// End of statement.
85+
if ($token->type === Token::TYPE_DELIMITER) {
86+
break;
87+
}
88+
89+
// Skipping whitespaces and comments.
90+
if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) {
91+
continue;
92+
}
93+
94+
if ($state === 0) {
95+
$expr->expr = Expression::parse($parser, $list);
96+
$state = 1;
97+
} elseif ($state === 1) {
98+
if (($token->type === Token::TYPE_KEYWORD)
99+
&& (($token->keyword === 'ASC') || ($token->keyword === 'DESC'))
100+
) {
101+
$expr->type = $token->keyword;
102+
} elseif (($token->type === Token::TYPE_OPERATOR)
103+
&& ($token->value === ',')
104+
) {
105+
if (!empty($expr->expr)) {
106+
$ret[] = $expr;
107+
}
108+
$expr = new self();
109+
$state = 0;
110+
} else {
111+
break;
112+
}
113+
}
114+
}
115+
116+
// Last iteration was not processed.
117+
if (!empty($expr->expr)) {
118+
$ret[] = $expr;
119+
}
120+
121+
--$list->idx;
122+
123+
return $ret;
124+
}
125+
126+
/**
127+
* @param GroupKeyword|GroupKeyword[] $component the component to be built
128+
* @param array $options parameters for building
129+
*
130+
* @return string
131+
*/
132+
public static function build($component, array $options = array())
133+
{
134+
if (is_array($component)) {
135+
return implode(', ', $component);
136+
}
137+
138+
return trim($component->expr . ' ' . $component->type);
139+
140+
}
141+
}

src/Parser.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ class Parser extends Core
168168
'options' => array('field' => 'table'),
169169
),
170170
'GROUP BY' => array(
171-
'class' => 'PhpMyAdmin\\SqlParser\\Components\\OrderKeyword',
171+
'class' => 'PhpMyAdmin\\SqlParser\\Components\\GroupKeyword',
172172
'field' => 'group',
173173
),
174174
'HAVING' => array(

tests/Components/GroupKeywordTest.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace PhpMyAdmin\SqlParser\Tests\Components;
4+
5+
use PhpMyAdmin\SqlParser\Components\Expression;
6+
use PhpMyAdmin\SqlParser\Components\GroupKeyword;
7+
use PhpMyAdmin\SqlParser\Tests\TestCase;
8+
9+
class GroupKeywordTest extends TestCase
10+
{
11+
public function testBuild()
12+
{
13+
$this->assertEquals(
14+
GroupKeyword::build(
15+
array(
16+
new GroupKeyword(new Expression('a'), 'ASC'),
17+
new GroupKeyword(new Expression('b'), 'DESC'),
18+
new GroupKeyword(new Expression('c')),
19+
)
20+
),
21+
'a ASC, b DESC, c'
22+
);
23+
}
24+
}

0 commit comments

Comments
 (0)