Skip to content

Commit 95ee230

Browse files
author
David Maechler
committed
fixing reserved sql names that where considered as expression nodes and never outputed. Add a new node Reserved.php to handle it. Add also the unit test to this case with group_concat
1 parent 911d23e commit 95ee230

File tree

3 files changed

+205
-3
lines changed

3 files changed

+205
-3
lines changed

src/SQLParser/Node/NodeFactory.php

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -292,14 +292,33 @@ public static function toObject(array $desc)
292292
}
293293

294294
return $expr;
295+
case ExpressionType::RESERVED:
296+
$res = new Reserved();
297+
$res->setBaseExpression($desc['base_expr']);
298+
299+
if (isset($desc['sub_tree'])) {
300+
$res->setSubTree(self::buildFromSubtree($desc['sub_tree']));
301+
}
302+
303+
if ($desc['expr_type'] == ExpressionType::BRACKET_EXPRESSION) {
304+
$res->setBrackets(true);
305+
}
306+
307+
// Debug:
308+
unset($desc['base_expr']);
309+
unset($desc['expr_type']);
310+
unset($desc['sub_tree']);
311+
unset($desc['alias']);
312+
unset($desc['direction']);
313+
if (!empty($desc)) {
314+
throw new \InvalidArgumentException('Unexpected parameters in exception: '.var_export($desc, true));
315+
}
295316

317+
return $res;
296318
case ExpressionType::USER_VARIABLE:
297319
case ExpressionType::SESSION_VARIABLE:
298320
case ExpressionType::GLOBAL_VARIABLE:
299321
case ExpressionType::LOCAL_VARIABLE:
300-
301-
case ExpressionType::RESERVED:
302-
303322
case ExpressionType::EXPRESSION:
304323
case ExpressionType::BRACKET_EXPRESSION:
305324
case ExpressionType::TABLE_EXPRESSION:

src/SQLParser/Node/Reserved.php

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
<?php
2+
3+
/**
4+
* expression-types.php.
5+
*
6+
*
7+
* Copyright (c) 2010-2013, Justin Swanhart
8+
* with contributions by André Rothe <[email protected], [email protected]>
9+
* and David Négrier <[email protected]>
10+
*
11+
* All rights reserved.
12+
*
13+
* Redistribution and use in source and binary forms, with or without modification,
14+
* are permitted provided that the following conditions are met:
15+
*
16+
* * Redistributions of source code must retain the above copyright notice,
17+
* this list of conditions and the following disclaimer.
18+
* * Redistributions in binary form must reproduce the above copyright notice,
19+
* this list of conditions and the following disclaimer in the documentation
20+
* and/or other materials provided with the distribution.
21+
*
22+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
23+
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24+
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
25+
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26+
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
27+
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28+
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30+
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31+
* DAMAGE.
32+
*/
33+
34+
namespace SQLParser\Node;
35+
36+
use Doctrine\DBAL\Connection;
37+
use Mouf\MoufInstanceDescriptor;
38+
use Mouf\MoufManager;
39+
use SQLParser\Node\Traverser\NodeTraverser;
40+
use SQLParser\Node\Traverser\VisitorInterface;
41+
42+
/**
43+
* This class represents a node that is an SQL expression.
44+
*
45+
* @author David Négrier <[email protected]>
46+
*/
47+
class Reserved implements NodeInterface
48+
{
49+
private $baseExpression;
50+
51+
/**
52+
* Returns the base expression (the string that generated this expression).
53+
*
54+
* @return string
55+
*/
56+
public function getBaseExpression()
57+
{
58+
return $this->baseExpression;
59+
}
60+
61+
/**
62+
* Sets the base expression (the string that generated this expression).
63+
*
64+
* @param string $baseExpression
65+
*/
66+
public function setBaseExpression($baseExpression)
67+
{
68+
$this->baseExpression = $baseExpression;
69+
}
70+
71+
private $subTree;
72+
73+
public function getSubTree()
74+
{
75+
return $this->subTree;
76+
}
77+
78+
/**
79+
* Sets the subtree.
80+
*
81+
* @Important
82+
*
83+
* @param array<NodeInterface>|NodeInterface $subTree
84+
*/
85+
public function setSubTree($subTree)
86+
{
87+
$this->subTree = $subTree;
88+
$this->subTree = NodeFactory::simplify($this->subTree);
89+
}
90+
91+
private $brackets = false;
92+
93+
/**
94+
* Returns true if the expression is between brackets.
95+
*
96+
* @return bool
97+
*/
98+
public function hasBrackets()
99+
{
100+
return $this->brackets;
101+
}
102+
103+
/**
104+
* Sets to true if the expression is between brackets.
105+
*
106+
* @Important
107+
*
108+
* @param bool $brackets
109+
*/
110+
public function setBrackets($brackets)
111+
{
112+
$this->brackets = $brackets;
113+
}
114+
115+
/**
116+
* Returns a Mouf instance descriptor describing this object.
117+
*
118+
* @param MoufManager $moufManager
119+
*
120+
* @return MoufInstanceDescriptor
121+
*/
122+
public function toInstanceDescriptor(MoufManager $moufManager)
123+
{
124+
$instanceDescriptor = $moufManager->createInstance(get_called_class());
125+
$instanceDescriptor->getProperty('baseExpression')->setValue(NodeFactory::nodeToInstanceDescriptor($this->baseExpression, $moufManager));
126+
$instanceDescriptor->getProperty('subTree')->setValue(NodeFactory::nodeToInstanceDescriptor($this->subTree, $moufManager));
127+
$instanceDescriptor->getProperty('brackets')->setValue(NodeFactory::nodeToInstanceDescriptor($this->brackets, $moufManager));
128+
129+
return $instanceDescriptor;
130+
}
131+
132+
/**
133+
* Renders the object as a SQL string.
134+
*
135+
* @param Connection $dbConnection
136+
* @param array $parameters
137+
* @param number $indent
138+
* @param int $conditionsMode
139+
*
140+
* @return string
141+
*/
142+
public function toSql(array $parameters = array(), Connection $dbConnection = null, $indent = 0, $conditionsMode = self::CONDITION_APPLY)
143+
{
144+
$sql = NodeFactory::toSql($this->subTree, $dbConnection, $parameters, ' ', false, $indent, $conditionsMode);
145+
146+
if($this->baseExpression) {
147+
$sql .= ' '.$this->baseExpression.' ';
148+
}
149+
150+
if ($this->brackets) {
151+
$sql = '('.$sql.')';
152+
}
153+
154+
return $sql;
155+
}
156+
157+
/**
158+
* Walks the tree of nodes, calling the visitor passed in parameter.
159+
*
160+
* @param VisitorInterface $visitor
161+
*/
162+
public function walk(VisitorInterface $visitor) {
163+
$node = $this;
164+
$result = $visitor->enterNode($node);
165+
if ($result instanceof NodeInterface) {
166+
$node = $result;
167+
}
168+
if ($result !== NodeTraverser::DONT_TRAVERSE_CHILDREN) {
169+
foreach ($this->subTree as $key => $operand) {
170+
$result2 = $operand->walk($visitor);
171+
if ($result2 === NodeTraverser::REMOVE_NODE) {
172+
unset($this->subTree[$key]);
173+
} elseif ($result2 instanceof NodeInterface) {
174+
$this->subTree[$key] = $result2;
175+
}
176+
}
177+
}
178+
return $visitor->leaveNode($node);
179+
}
180+
}

tests/Mouf/Database/MagicQueryTest.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ public function testStandardSelect()
1212
{
1313
$magicQuery = new MagicQuery();
1414

15+
$sql = "SELECT GROUP_CONCAT(u.id SEPARATOR ', ') AS ids FROM users";
16+
$this->assertEquals("SELECT GROUP_CONCAT(u.id SEPARATOR ', ') AS ids FROM users", self::simplifySql($magicQuery->build($sql)));
17+
1518
$sql = "SELECT id FROM users WHERE name LIKE :name LIMIT :offset, :limit";
1619
$this->assertEquals("SELECT id FROM users WHERE name LIKE 'foo'", self::simplifySql($magicQuery->build($sql, ['name' => 'foo'])));
1720

0 commit comments

Comments
 (0)