99namespace SqlParser \Statements ;
1010
1111use SqlParser \Statement ;
12+ use SqlParser \Parser ;
13+ use SqlParser \Token ;
14+ use SqlParser \TokensList ;
1215use SqlParser \Components \ArrayObj ;
1316use SqlParser \Components \Expression ;
17+ use SqlParser \Components \ExpressionArray ;
1418use SqlParser \Components \Limit ;
1519use SqlParser \Components \OrderKeyword ;
1620use SqlParser \Components \Condition ;
21+ use SqlParser \Components \OptionsArray ;
1722
1823/**
1924 * `DELETE` statement.
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