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
@@ -45,29 +65,25 @@ class DeleteStatement extends Statement
4565 );
4666
4767 /**
48- * The clauses of this statement, in order .
68+ * Table(s) used as sources for this statement .
4969 *
50- * @see Statement::$CLAUSES
70+ * @var Expression[]
71+ */
72+ public $ from ;
73+
74+ /**
75+ * Tables used as sources for this statement
5176 *
52- * @var array
77+ * @var Expression[]
5378 */
54- public static $ CLAUSES = array (
55- 'DELETE ' => array ('DELETE ' , 2 ),
56- // Used for options.
57- '_OPTIONS ' => array ('_OPTIONS ' , 1 ),
58- 'FROM ' => array ('FROM ' , 3 ),
59- 'PARTITION ' => array ('PARTITION ' , 3 ),
60- 'WHERE ' => array ('WHERE ' , 3 ),
61- 'ORDER BY ' => array ('ORDER BY ' , 3 ),
62- 'LIMIT ' => array ('LIMIT ' , 3 ),
63- );
79+ public $ using ;
6480
6581 /**
66- * Tables used as sources for this statement.
82+ * Columns used in this statement
6783 *
6884 * @var Expression[]
6985 */
70- public $ from ;
86+ public $ columns ;
7187
7288 /**
7389 * Partitions used as source for this statement.
@@ -96,4 +112,230 @@ class DeleteStatement extends Statement
96112 * @var Limit
97113 */
98114 public $ limit ;
115+
116+
117+ /**
118+ * @return string
119+ */
120+ public function build ()
121+ {
122+ $ ret = 'DELETE ' . OptionsArray::build ($ this ->options );
123+
124+ if ($ this ->columns != NULL && count ($ this ->columns ) > 0 ) {
125+ $ ret .= ' ' . ExpressionArray::build ($ this ->columns );
126+ }
127+ if ($ this ->from != NULL && count ($ this ->from ) > 0 ) {
128+ $ ret .= ' FROM ' . ExpressionArray::build ($ this ->from );
129+ }
130+ if ($ this ->using != NULL && count ($ this ->using ) > 0 ) {
131+ $ ret .= ' USING ' . ExpressionArray::build ($ this ->using );
132+ }
133+ if ($ this ->where != NULL && count ($ this ->where ) > 0 ) {
134+ $ ret .= ' WHERE ' . Condition::build ($ this ->where );
135+ }
136+ if ($ this ->order != NULL && count ($ this ->order ) > 0 ) {
137+ $ ret .= ' ORDER BY ' . ExpressionArray::build ($ this ->order );
138+ }
139+ if ($ this ->limit != NULL && count ($ this ->limit ) > 0 ) {
140+ $ ret .= ' LIMIT ' . Limit::build ($ this ->limit );
141+ }
142+
143+ return $ ret ;
144+
145+ }
146+
147+
148+ /**
149+ * @param Parser $parser The instance that requests parsing.
150+ * @param TokensList $list The list of tokens to be parsed.
151+ *
152+ * @return void
153+ */
154+ public function parse (Parser $ parser , TokensList $ list )
155+ {
156+ ++$ list ->idx ; // Skipping `DELETE`.
157+
158+ // parse any options if provided
159+ $ this ->options = OptionsArray::parse (
160+ $ parser ,
161+ $ list ,
162+ static ::$ OPTIONS
163+ );
164+ ++$ list ->idx ;
165+
166+ /**
167+ * The state of the parser.
168+ *
169+ * Below are the states of the parser.
170+ *
171+ * 0 ---------------------------------[ FROM ]----------------------------------> 2
172+ * 0 ------------------------------[ table[.*] ]--------------------------------> 1
173+ * 1 ---------------------------------[ FROM ]----------------------------------> 2
174+ * 2 --------------------------------[ USING ]----------------------------------> 3
175+ * 2 --------------------------------[ WHERE ]----------------------------------> 4
176+ * 2 --------------------------------[ ORDER ]----------------------------------> 5
177+ * 2 --------------------------------[ LIMIT ]----------------------------------> 6
178+ *
179+ * @var int $state
180+ */
181+ $ state = 0 ;
182+
183+ for (; $ list ->idx < $ list ->count ; ++$ list ->idx ) {
184+ /**
185+ * Token parsed at this moment.
186+ *
187+ * @var Token $token
188+ */
189+ $ token = $ list ->tokens [$ list ->idx ];
190+
191+ // End of statement.
192+ if ($ token ->type === Token::TYPE_DELIMITER ) {
193+ break ;
194+ }
195+
196+ // Skipping whitespaces and comments.
197+ if (($ token ->type === Token::TYPE_WHITESPACE ) || ($ token ->type === Token::TYPE_COMMENT )) {
198+ continue ;
199+ }
200+
201+ if ($ state === 0 ) {
202+ if ($ token ->type === Token::TYPE_KEYWORD
203+ && $ token ->value !== 'FROM '
204+ ) {
205+ $ parser ->error (__ ('Unexpected keyword. ' ), $ token );
206+ break ;
207+ } elseif ($ token ->type === Token::TYPE_KEYWORD
208+ && $ token ->value === 'FROM '
209+ ) {
210+ ++$ list ->idx ; // Skip 'FROM'
211+ $ this ->from = ExpressionArray::parse ($ parser , $ list );
212+ $ state = 2 ;
213+ } else {
214+ $ this ->columns = ExpressionArray::parse ($ parser , $ list );
215+ $ state = 1 ;
216+ }
217+ } elseif ($ state === 1 ) {
218+ if ($ token ->type === Token::TYPE_KEYWORD
219+ && $ token ->value !== 'FROM '
220+ ) {
221+ $ parser ->error (__ ('Unexpected keyword. ' ), $ token );
222+ break ;
223+ } elseif ($ token ->type === Token::TYPE_KEYWORD
224+ && $ token ->value === 'FROM '
225+ ) {
226+ ++$ list ->idx ; // Skip 'FROM'
227+ $ this ->from = ExpressionArray::parse ($ parser , $ list );
228+ $ state = 2 ;
229+ } elseif ($ token ->type === Token::TYPE_KEYWORD ) {
230+ $ parser ->error (__ ('Unexpected keyword. ' ), $ token );
231+ break ;
232+ } else {
233+ $ parser ->error (__ ('Unexpected token. ' ), $ token );
234+ break ;
235+ }
236+ } elseif ($ state === 2 ) {
237+ if ($ token ->type === Token::TYPE_KEYWORD
238+ && $ token ->value === 'USING '
239+ ) {
240+ ++$ list ->idx ; // Skip 'FROM'
241+ $ this ->using = ExpressionArray::parse ($ parser , $ list );
242+ $ state = 3 ;
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+ } else {
265+ $ parser ->error (__ ('Unexpected token. ' ), $ token );
266+ break ;
267+ }
268+ } elseif ($ state === 3 ) {
269+ if ($ token ->type === Token::TYPE_KEYWORD
270+ && $ token ->value === 'WHERE '
271+ ) {
272+ ++$ list ->idx ; // Skip 'WHERE'
273+ $ this ->where = Condition::parse ($ parser , $ list );
274+ $ state = 4 ;
275+ } elseif ($ token ->type === Token::TYPE_KEYWORD
276+ && $ token ->value === 'ORDER BY '
277+ ) {
278+ ++$ list ->idx ; // Skip 'ORDER BY'
279+ $ this ->order = OrderKeyword::parse ($ parser , $ list );
280+ $ state = 5 ;
281+ } elseif ($ token ->type === Token::TYPE_KEYWORD
282+ && $ token ->value === 'LIMIT '
283+ ) {
284+ ++$ list ->idx ; // Skip 'LIMIT'
285+ $ this ->limit = Limit::parse ($ parser , $ list );
286+ $ state = 6 ;
287+ } elseif ($ token ->type === Token::TYPE_KEYWORD ) {
288+ $ parser ->error (__ ('Unexpected keyword. ' ), $ token );
289+ break ;
290+ } else {
291+ $ parser ->error (__ ('Unexpected token. ' ), $ token );
292+ break ;
293+ }
294+ } elseif ($ state === 4 ) {
295+ if ($ token ->type === Token::TYPE_KEYWORD
296+ && $ token ->value === 'ORDER BY '
297+ ) {
298+ ++$ list ->idx ; // Skip 'ORDER BY'
299+ $ this ->order = OrderKeyword::parse ($ parser , $ list );
300+ $ state = 5 ;
301+ } elseif ($ token ->type === Token::TYPE_KEYWORD
302+ && $ token ->value === 'LIMIT '
303+ ) {
304+ ++$ list ->idx ; // Skip 'LIMIT'
305+ $ this ->limit = Limit::parse ($ parser , $ list );
306+ $ state = 6 ;
307+ } elseif ($ token ->type === Token::TYPE_KEYWORD ) {
308+ $ parser ->error (__ ('Unexpected keyword. ' ), $ token );
309+ break ;
310+ } else {
311+ $ parser ->error (__ ('Unexpected token. ' ), $ token );
312+ break ;
313+ }
314+ } elseif ($ state === 5 ) {
315+ if ($ token ->type === Token::TYPE_KEYWORD
316+ && $ token ->value === 'LIMIT '
317+ ) {
318+ ++$ list ->idx ; // Skip 'LIMIT'
319+ $ this ->limit = Limit::parse ($ parser , $ list );
320+ $ state = 6 ;
321+ } elseif ($ token ->type === Token::TYPE_KEYWORD ) {
322+ $ parser ->error (__ ('Unexpected keyword. ' ), $ token );
323+ break ;
324+ } else {
325+ $ parser ->error (__ ('Unexpected token. ' ), $ token );
326+ break ;
327+ }
328+ }
329+ }
330+
331+ if ($ state >= 2 ) {
332+ foreach ($ this ->from as $ from_expr ) {
333+ $ from_expr ->database = $ from_expr ->table ;
334+ $ from_expr ->table = $ from_expr ->column ;
335+ $ from_expr ->column = null ;
336+ }
337+ }
338+
339+ --$ list ->idx ;
340+ }
99341}
0 commit comments