Skip to content

Commit 29a9dc1

Browse files
authored
Merge pull request #87 from devenbansod/delete_statement
Implement parsing and building for Delete Statement
2 parents 1b72bd3 + 98679cb commit 29a9dc1

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+425
-33
lines changed

src/Components/Limit.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,6 @@ public static function parse(Parser $parser, TokensList $list, array $options =
126126
*/
127127
public static function build($component, array $options = array())
128128
{
129-
if (empty($component->offset)) {
130-
return (string) $component->rowCount;
131-
} else {
132-
return $component->offset . ', ' . $component->rowCount;
133-
}
129+
return $component->offset . ', ' . $component->rowCount;
134130
}
135131
}

src/Statements/DeleteStatement.php

Lines changed: 247 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,16 @@
99
namespace SqlParser\Statements;
1010

1111
use SqlParser\Statement;
12+
use SqlParser\Parser;
13+
use SqlParser\Token;
14+
use SqlParser\TokensList;
1215
use SqlParser\Components\ArrayObj;
1316
use SqlParser\Components\Expression;
17+
use SqlParser\Components\ExpressionArray;
1418
use SqlParser\Components\Limit;
1519
use SqlParser\Components\OrderKeyword;
1620
use SqlParser\Components\Condition;
21+
use SqlParser\Components\OptionsArray;
1722

1823
/**
1924
* `DELETE` statement.
@@ -24,6 +29,21 @@
2429
* [ORDER BY ...]
2530
* [LIMIT row_count]
2631
*
32+
* Multi-table syntax
33+
*
34+
* DELETE [LOW_PRIORITY] [QUICK] [IGNORE]
35+
* tbl_name[.*] [, tbl_name[.*]] ...
36+
* FROM table_references
37+
* [WHERE where_condition]
38+
*
39+
* OR
40+
*
41+
* DELETE [LOW_PRIORITY] [QUICK] [IGNORE]
42+
* FROM tbl_name[.*] [, tbl_name[.*]] ...
43+
* USING table_references
44+
* [WHERE where_condition]
45+
*
46+
*
2747
* @category Statements
2848
* @package SqlParser
2949
* @subpackage Statements
@@ -44,29 +64,25 @@ class DeleteStatement extends Statement
4464
);
4565

4666
/**
47-
* The clauses of this statement, in order.
67+
* Table(s) used as sources for this statement.
4868
*
49-
* @see Statement::$CLAUSES
69+
* @var Expression[]
70+
*/
71+
public $from;
72+
73+
/**
74+
* Tables used as sources for this statement
5075
*
51-
* @var array
76+
* @var Expression[]
5277
*/
53-
public static $CLAUSES = array(
54-
'DELETE' => array('DELETE', 2),
55-
// Used for options.
56-
'_OPTIONS' => array('_OPTIONS', 1),
57-
'FROM' => array('FROM', 3),
58-
'PARTITION' => array('PARTITION', 3),
59-
'WHERE' => array('WHERE', 3),
60-
'ORDER BY' => array('ORDER BY', 3),
61-
'LIMIT' => array('LIMIT', 3),
62-
);
78+
public $using;
6379

6480
/**
65-
* Tables used as sources for this statement.
81+
* Columns used in this statement
6682
*
6783
* @var Expression[]
6884
*/
69-
public $from;
85+
public $columns;
7086

7187
/**
7288
* Partitions used as source for this statement.
@@ -95,4 +111,220 @@ class DeleteStatement extends Statement
95111
* @var Limit
96112
*/
97113
public $limit;
114+
115+
116+
/**
117+
* @return string
118+
*/
119+
public function build()
120+
{
121+
$ret = 'DELETE ' . OptionsArray::build($this->options);
122+
123+
if ($this->columns != NULL && count($this->columns) > 0) {
124+
$ret .= ' ' . ExpressionArray::build($this->columns);
125+
}
126+
if ($this->from != NULL && count($this->from) > 0) {
127+
$ret .= ' FROM ' . ExpressionArray::build($this->from);
128+
}
129+
if ($this->using != NULL && count($this->using) > 0) {
130+
$ret .= ' USING ' . ExpressionArray::build($this->using);
131+
}
132+
if ($this->where != NULL && count($this->where) > 0) {
133+
$ret .= ' WHERE ' . Condition::build($this->where);
134+
}
135+
if ($this->order != NULL && count($this->order) > 0) {
136+
$ret .= ' ORDER BY ' . ExpressionArray::build($this->order);
137+
}
138+
if ($this->limit != NULL && count($this->limit) > 0) {
139+
$ret .= ' LIMIT ' . Limit::build($this->limit);
140+
}
141+
142+
return $ret;
143+
144+
}
145+
146+
147+
/**
148+
* @param Parser $parser The instance that requests parsing.
149+
* @param TokensList $list The list of tokens to be parsed.
150+
*
151+
* @return void
152+
*/
153+
public function parse(Parser $parser, TokensList $list)
154+
{
155+
++$list->idx; // Skipping `DELETE`.
156+
157+
// parse any options if provided
158+
$this->options = OptionsArray::parse(
159+
$parser,
160+
$list,
161+
static::$OPTIONS
162+
);
163+
++$list->idx;
164+
165+
/**
166+
* The state of the parser.
167+
*
168+
* Below are the states of the parser.
169+
*
170+
* 0 ---------------------------------[ FROM ]----------------------------------> 2
171+
* 0 ------------------------------[ table[.*] ]--------------------------------> 1
172+
* 1 ---------------------------------[ FROM ]----------------------------------> 2
173+
* 2 --------------------------------[ USING ]----------------------------------> 3
174+
* 2 --------------------------------[ WHERE ]----------------------------------> 4
175+
* 2 --------------------------------[ ORDER ]----------------------------------> 5
176+
* 2 --------------------------------[ LIMIT ]----------------------------------> 6
177+
*
178+
* @var int $state
179+
*/
180+
$state = 0;
181+
182+
/**
183+
* If the query is multi-table or not
184+
*
185+
* @var bool $multiTable
186+
*/
187+
$multiTable = false;
188+
189+
for (; $list->idx < $list->count; ++$list->idx) {
190+
/**
191+
* Token parsed at this moment.
192+
*
193+
* @var Token $token
194+
*/
195+
$token = $list->tokens[$list->idx];
196+
197+
// End of statement.
198+
if ($token->type === Token::TYPE_DELIMITER) {
199+
break;
200+
}
201+
202+
if ($state === 0) {
203+
if ($token->type === Token::TYPE_KEYWORD
204+
&& $token->value !== 'FROM'
205+
) {
206+
$parser->error(__('Unexpected keyword.'), $token);
207+
break;
208+
} elseif ($token->type === Token::TYPE_KEYWORD
209+
&& $token->value === 'FROM'
210+
) {
211+
++$list->idx; // Skip 'FROM'
212+
$this->from = ExpressionArray::parse($parser, $list);
213+
$state = 2;
214+
} else {
215+
$this->columns = ExpressionArray::parse($parser, $list);
216+
$state = 1;
217+
}
218+
} elseif ($state === 1) {
219+
if ($token->type === Token::TYPE_KEYWORD
220+
&& $token->value !== 'FROM'
221+
) {
222+
$parser->error(__('Unexpected keyword.'), $token);
223+
break;
224+
} elseif ($token->type === Token::TYPE_KEYWORD
225+
&& $token->value === 'FROM'
226+
) {
227+
++$list->idx; // Skip 'FROM'
228+
$this->from = ExpressionArray::parse($parser, $list);
229+
$state = 2;
230+
} else {
231+
$parser->error(__('Unexpected token.'), $token);
232+
break;
233+
}
234+
} elseif ($state === 2) {
235+
if ($token->type === Token::TYPE_KEYWORD
236+
&& $token->value === 'USING'
237+
) {
238+
++$list->idx; // Skip 'USING'
239+
$this->using = ExpressionArray::parse($parser, $list);
240+
$state = 3;
241+
242+
$multiTable = true;
243+
} elseif ($token->type === Token::TYPE_KEYWORD
244+
&& $token->value === 'WHERE'
245+
) {
246+
++$list->idx; // Skip 'WHERE'
247+
$this->where = Condition::parse($parser, $list);
248+
$state = 4;
249+
} elseif ($token->type === Token::TYPE_KEYWORD
250+
&& $token->value === 'ORDER BY'
251+
) {
252+
++$list->idx; // Skip 'ORDER BY'
253+
$this->order = OrderKeyword::parse($parser, $list);
254+
$state = 5;
255+
} elseif ($token->type === Token::TYPE_KEYWORD
256+
&& $token->value === 'LIMIT'
257+
) {
258+
++$list->idx; // Skip 'LIMIT'
259+
$this->limit = Limit::parse($parser, $list);
260+
$state = 6;
261+
} elseif ($token->type === Token::TYPE_KEYWORD) {
262+
$parser->error(__('Unexpected keyword.'), $token);
263+
break;
264+
}
265+
} elseif ($state === 3) {
266+
if ($token->type === Token::TYPE_KEYWORD
267+
&& $token->value === 'WHERE'
268+
) {
269+
++$list->idx; // Skip 'WHERE'
270+
$this->where = Condition::parse($parser, $list);
271+
$state = 4;
272+
} elseif ($token->type === Token::TYPE_KEYWORD) {
273+
$parser->error(__('Unexpected keyword.'), $token);
274+
break;
275+
} else {
276+
$parser->error(__('Unexpected token.'), $token);
277+
break;
278+
}
279+
} elseif ($state === 4) {
280+
if ($multiTable === true
281+
&& $token->type === Token::TYPE_KEYWORD
282+
) {
283+
$parser->error(
284+
__('This type of clause is not valid in Multi-table queries.'),
285+
$token
286+
);
287+
break;
288+
}
289+
290+
if ($token->type === Token::TYPE_KEYWORD
291+
&& $token->value === 'ORDER BY'
292+
) {
293+
++$list->idx; // Skip 'ORDER BY'
294+
$this->order = OrderKeyword::parse($parser, $list);
295+
$state = 5;
296+
} elseif ($token->type === Token::TYPE_KEYWORD
297+
&& $token->value === 'LIMIT'
298+
) {
299+
++$list->idx; // Skip 'LIMIT'
300+
$this->limit = Limit::parse($parser, $list);
301+
$state = 6;
302+
} elseif ($token->type === Token::TYPE_KEYWORD) {
303+
$parser->error(__('Unexpected keyword.'), $token);
304+
break;
305+
}
306+
} elseif ($state === 5) {
307+
if ($token->type === Token::TYPE_KEYWORD
308+
&& $token->value === 'LIMIT'
309+
) {
310+
++$list->idx; // Skip 'LIMIT'
311+
$this->limit = Limit::parse($parser, $list);
312+
$state = 6;
313+
} elseif ($token->type === Token::TYPE_KEYWORD) {
314+
$parser->error(__('Unexpected keyword.'), $token);
315+
break;
316+
}
317+
}
318+
}
319+
320+
if ($state >= 2) {
321+
foreach ($this->from as $from_expr) {
322+
$from_expr->database = $from_expr->table;
323+
$from_expr->table = $from_expr->column;
324+
$from_expr->column = null;
325+
}
326+
}
327+
328+
--$list->idx;
329+
}
98330
}

0 commit comments

Comments
 (0)