Skip to main content

oak_sql/parser/
mod.rs

1/// Element types for SQL.
2pub mod element_type;
3
4use crate::{SqlElementType, SqlLanguage};
5use oak_core::{
6    GreenNode, OakError, Parser, ParserState, TextEdit, TokenType,
7    parser::{
8        ParseCache, ParseOutput, parse_with_lexer,
9        pratt::{Associativity, Pratt, PrattParser, binary},
10    },
11    source::Source,
12};
13
14/// Parser for SQL.
15///
16/// This parser implements the [`Parser`] trait and uses a recursive descent
17/// approach combined with a Pratt parser for expressions. It handles various
18/// SQL dialects based on the provided [`SqlLanguage`] configuration.
19///
20/// # Supported Dialects
21///
22/// - Standard SQL
23/// - MySQL (with backticks and case-insensitive matching)
24/// - PostgreSQL
25/// - SQLite
26/// - SQL Server
27pub struct SqlParser<'config> {
28    pub(crate) config: &'config SqlLanguage,
29}
30
31impl<'config> SqlParser<'config> {
32    /// Creates a new `SqlParser` with the given configuration.
33    pub fn new(config: &'config SqlLanguage) -> Self {
34        Self { config }
35    }
36}
37
38pub(crate) type State<'a, S> = ParserState<'a, SqlLanguage, S>;
39
40impl<'config> Pratt<SqlLanguage> for SqlParser<'config> {
41    fn primary<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, SqlLanguage> {
42        use crate::lexer::SqlTokenType::*;
43        let cp = state.checkpoint();
44        match state.peek_kind() {
45            Some(Identifier_) => {
46                state.bump();
47                state.finish_at(cp, SqlElementType::Identifier)
48            }
49            Some(LeftBracket) => {
50                state.bump();
51                while state.not_at_end() && state.peek_kind() != Some(RightBracket) {
52                    PrattParser::parse(state, 0, self);
53                    if !state.eat(Comma) {
54                        break;
55                    }
56                }
57                state.expect(RightBracket).ok();
58                state.finish_at(cp, SqlElementType::Expression)
59            }
60            Some(NumberLiteral) | Some(FloatLiteral) | Some(StringLiteral) | Some(BooleanLiteral) | Some(NullLiteral) | Some(True) | Some(False) | Some(Null) => {
61                state.bump();
62                state.finish_at(cp, SqlElementType::Expression)
63            }
64            Some(LeftParen) => {
65                state.bump();
66                PrattParser::parse(state, 0, self);
67                state.expect(RightParen).ok();
68                state.finish_at(cp, SqlElementType::Expression)
69            }
70            _ => {
71                state.bump();
72                state.finish_at(cp, SqlElementType::ErrorNode)
73            }
74        }
75    }
76
77    fn prefix<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> &'a GreenNode<'a, SqlLanguage> {
78        self.primary(state)
79    }
80
81    fn infix<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>, left: &'a GreenNode<'a, SqlLanguage>, min_precedence: u8) -> Option<&'a GreenNode<'a, SqlLanguage>> {
82        use crate::lexer::SqlTokenType::*;
83        let kind = state.peek_kind()?;
84
85        let (prec, assoc) = match kind {
86            Or => (1, Associativity::Left),
87            And => (2, Associativity::Left),
88            Equal | NotEqual | Less | Greater | LessEqual | GreaterEqual | Like | In | Between | Is => (3, Associativity::Left),
89            Concat => (4, Associativity::Left),
90            Plus | Minus => (10, Associativity::Left),
91            Star | Slash | Percent => (11, Associativity::Left),
92            DoubleColon => (15, Associativity::Left),
93            _ => return None,
94        };
95
96        if prec < min_precedence {
97            return None;
98        }
99
100        let expr_kind = SqlElementType::Expression;
101        Some(binary(state, left, kind, prec, assoc, expr_kind, |s, p| PrattParser::parse(s, p, self)))
102    }
103}
104
105impl<'config> SqlParser<'config> {
106    fn parse_statement<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
107        use crate::lexer::SqlTokenType::*;
108        match state.peek_kind() {
109            Some(Select) => self.parse_select(state)?,
110            Some(Insert) => self.parse_insert(state)?,
111            Some(Update) => self.parse_update(state)?,
112            Some(Delete) => self.parse_delete(state)?,
113            Some(Create) => self.parse_create(state)?,
114            Some(Drop) => self.parse_drop(state)?,
115            Some(Alter) => self.parse_alter(state)?,
116            Some(Explain) => self.parse_explain(state)?,
117            Some(Begin) | Some(Commit) | Some(Rollback) | Some(Transaction) => self.parse_transaction(state)?,
118            Some(Pragma) => self.parse_pragma(state)?,
119            Some(Show) => self.parse_show(state)?,
120            Some(Set) => self.parse_set(state)?,
121            _ => {
122                let cp = state.checkpoint();
123                state.advance_until(Semicolon);
124                state.eat(Semicolon);
125                state.finish_at(cp, SqlElementType::ErrorNode);
126            }
127        }
128        Ok(())
129    }
130
131    fn parse_set<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
132        use crate::lexer::SqlTokenType::*;
133        let cp = state.checkpoint();
134        state.expect(Set).ok();
135        state.expect(Identifier_).ok();
136        state.expect(Equal).ok();
137        PrattParser::parse(state, 0, self);
138        state.eat(Semicolon);
139        state.finish_at(cp, SqlElementType::Expression);
140        Ok(())
141    }
142
143    fn parse_explain<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
144        use crate::lexer::SqlTokenType::*;
145        let cp = state.checkpoint();
146        state.expect(Explain).ok();
147        self.parse_statement(state)?;
148        state.finish_at(cp, SqlElementType::ExplainStatement);
149        Ok(())
150    }
151
152    fn parse_transaction<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
153        use crate::lexer::SqlTokenType::*;
154        let cp = state.checkpoint();
155        match state.peek_kind() {
156            Some(Begin) => {
157                state.bump();
158                state.eat(Transaction);
159            }
160            Some(Commit) => {
161                state.bump();
162                state.eat(Transaction);
163            }
164            Some(Rollback) => {
165                state.bump();
166                state.eat(Transaction);
167            }
168            Some(Transaction) => {
169                state.bump();
170            }
171            _ => return Err(OakError::custom_error("Expected BEGIN, COMMIT, or ROLLBACK")),
172        }
173        state.eat(Semicolon);
174        state.finish_at(cp, SqlElementType::TransactionStatement);
175        Ok(())
176    }
177
178    fn parse_pragma<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
179        use crate::lexer::SqlTokenType::*;
180        let cp = state.checkpoint();
181        state.expect(Pragma).ok();
182        state.expect(Identifier_).ok();
183        if state.eat(Equal) || state.eat(LeftParen) {
184            PrattParser::parse(state, 0, self);
185            state.eat(RightParen);
186        }
187        state.eat(Semicolon);
188        state.finish_at(cp, SqlElementType::PragmaStatement);
189        Ok(())
190    }
191
192    fn parse_show<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
193        use crate::lexer::SqlTokenType::*;
194        let cp = state.checkpoint();
195        state.expect(Show).ok();
196        state.expect(Identifier_).ok();
197        state.eat(Semicolon);
198        state.finish_at(cp, SqlElementType::ShowStatement);
199        Ok(())
200    }
201
202    fn parse_select<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
203        use crate::lexer::SqlTokenType::*;
204        let cp = state.checkpoint();
205        state.expect(Select).ok();
206        state.eat(Distinct);
207        state.eat(All);
208
209        // Parse Select Items
210        while state.not_at_end() && state.peek_kind() != Some(From) {
211            let item_cp = state.checkpoint();
212            if state.eat(Star) {
213                // All columns
214            }
215            else {
216                PrattParser::parse(state, 0, self);
217                if state.eat(As) {
218                    state.expect(Identifier_).ok();
219                }
220                else if state.peek_kind() == Some(Identifier_) {
221                    state.bump();
222                }
223            }
224            state.finish_at(item_cp, SqlElementType::SelectItem);
225
226            if !state.eat(Comma) {
227                break;
228            }
229        }
230
231        if state.eat(From) {
232            let table_cp = state.checkpoint();
233            state.expect(Identifier_).ok(); // TableName
234            state.finish_at(table_cp, SqlElementType::TableName);
235
236            // Parse JOIN clauses
237            while let Some(kind) = state.peek_kind() {
238                if matches!(kind, Join | Inner | Left | Right | Full) {
239                    let join_cp = state.checkpoint();
240                    if kind != Join {
241                        state.bump(); // Inner, Left, etc.
242                        state.eat(Outer);
243                    }
244                    state.expect(Join).ok();
245
246                    let table_cp = state.checkpoint();
247                    state.expect(Identifier_).ok(); // Joined TableName
248                    state.finish_at(table_cp, SqlElementType::TableName);
249
250                    if state.eat(On) {
251                        PrattParser::parse(state, 0, self); // Join condition
252                    }
253                    state.finish_at(join_cp, SqlElementType::JoinClause);
254                }
255                else {
256                    break;
257                }
258            }
259        }
260
261        if state.eat(Where) {
262            PrattParser::parse(state, 0, self);
263        }
264
265        if state.eat(Group) {
266            let group_cp = state.checkpoint();
267            state.expect(By).ok();
268            while state.not_at_end() {
269                PrattParser::parse(state, 0, self);
270                if !state.eat(Comma) {
271                    break;
272                }
273            }
274            state.finish_at(group_cp, SqlElementType::GroupByClause);
275        }
276
277        if state.eat(Having) {
278            let having_cp = state.checkpoint();
279            PrattParser::parse(state, 0, self);
280            state.finish_at(having_cp, SqlElementType::HavingClause);
281        }
282
283        if state.eat(Order) {
284            let order_cp = state.checkpoint();
285            state.expect(By).ok();
286            while state.not_at_end() {
287                PrattParser::parse(state, 0, self);
288                if state.eat(Asc) || state.eat(Desc) {
289                    // Handled
290                }
291                if !state.eat(Comma) {
292                    break;
293                }
294            }
295            state.finish_at(order_cp, SqlElementType::OrderByClause);
296        }
297
298        if state.eat(Limit) {
299            let limit_cp = state.checkpoint();
300            state.expect(NumberLiteral).ok();
301            if state.eat(Offset) {
302                state.expect(NumberLiteral).ok();
303            }
304            state.finish_at(limit_cp, SqlElementType::LimitClause);
305        }
306        else if state.eat(Offset) {
307            let offset_cp = state.checkpoint();
308            state.expect(NumberLiteral).ok();
309            state.finish_at(offset_cp, SqlElementType::LimitClause);
310        }
311
312        state.eat(Semicolon);
313        state.finish_at(cp, SqlElementType::SelectStatement);
314        Ok(())
315    }
316
317    fn parse_insert<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
318        use crate::lexer::SqlTokenType::*;
319        let cp = state.checkpoint();
320        state.expect(Insert).ok();
321        state.eat(Into);
322
323        let table_cp = state.checkpoint();
324        state.expect(Identifier_).ok(); // TableName
325        state.finish_at(table_cp, SqlElementType::TableName);
326
327        if state.eat(LeftParen) {
328            while state.not_at_end() && state.peek_kind() != Some(RightParen) {
329                let col_cp = state.checkpoint();
330                state.expect(Identifier_).ok();
331                state.finish_at(col_cp, SqlElementType::ColumnName);
332                if !state.eat(Comma) {
333                    break;
334                }
335            }
336            state.expect(RightParen).ok();
337        }
338
339        if state.eat(Values) {
340            let values_cp = state.checkpoint();
341            while state.eat(LeftParen) {
342                let value_list_cp = state.checkpoint();
343                while state.not_at_end() && state.peek_kind() != Some(RightParen) {
344                    PrattParser::parse(state, 0, self);
345                    if !state.eat(Comma) {
346                        break;
347                    }
348                }
349                state.expect(RightParen).ok();
350                state.finish_at(value_list_cp, SqlElementType::ValueList);
351
352                if !state.eat(Comma) {
353                    break;
354                }
355            }
356            state.finish_at(values_cp, SqlElementType::ValueList);
357        }
358        else if state.peek_kind() == Some(Select) {
359            self.parse_select(state)?;
360        }
361
362        self.parse_on_conflict(state);
363        self.parse_returning_clause(state);
364
365        state.eat(Semicolon);
366        state.finish_at(cp, SqlElementType::InsertStatement);
367        Ok(())
368    }
369
370    fn parse_on_conflict<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
371        use crate::lexer::SqlTokenType::*;
372        if state.eat(On) {
373            if state.eat(Conflict) {
374                let cp = state.checkpoint();
375                // (col1, col2)
376                if state.eat(LeftParen) {
377                    while state.not_at_end() && state.peek_kind() != Some(RightParen) {
378                        state.expect(Identifier_).ok();
379                        if !state.eat(Comma) {
380                            break;
381                        }
382                    }
383                    state.expect(RightParen).ok();
384                }
385
386                if state.eat(Do) {
387                    if state.eat(Nothing) {
388                        // DO NOTHING
389                    }
390                    else if state.eat(Update) {
391                        state.expect(Set).ok();
392                        while state.not_at_end() && state.peek_kind() != Some(Where) && state.peek_kind() != Some(Semicolon) && state.peek_kind() != Some(Returning) {
393                            let assign_cp = state.checkpoint();
394                            state.expect(Identifier_).ok();
395                            state.expect(Equal).ok();
396                            PrattParser::parse(state, 0, self);
397                            state.finish_at(assign_cp, SqlElementType::Assignment);
398                            if !state.eat(Comma) {
399                                break;
400                            }
401                        }
402                        if state.eat(Where) {
403                            PrattParser::parse(state, 0, self);
404                        }
405                    }
406                }
407                state.finish_at(cp, SqlElementType::ConflictClause);
408            }
409        }
410    }
411
412    fn parse_returning_clause<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
413        use crate::lexer::SqlTokenType::*;
414        if state.eat(Returning) {
415            let cp = state.checkpoint();
416            while state.not_at_end() && state.peek_kind() != Some(Semicolon) {
417                if state.eat(Star) {
418                    // All columns
419                }
420                else {
421                    PrattParser::parse(state, 0, self);
422                    if state.eat(As) {
423                        state.expect(Identifier_).ok();
424                    }
425                    else if state.peek_kind() == Some(Identifier_) {
426                        state.bump();
427                    }
428                }
429                if !state.eat(Comma) {
430                    break;
431                }
432            }
433            state.finish_at(cp, SqlElementType::ReturningClause);
434        }
435    }
436
437    fn parse_update<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
438        use crate::lexer::SqlTokenType::*;
439        let cp = state.checkpoint();
440        state.expect(Update).ok();
441
442        let table_cp = state.checkpoint();
443        state.expect(Identifier_).ok(); // TableName
444        state.finish_at(table_cp, SqlElementType::TableName);
445
446        if state.eat(Set) {
447            while state.not_at_end() && state.peek_kind() != Some(Where) && state.peek_kind() != Some(Semicolon) {
448                let assign_cp = state.checkpoint();
449
450                let col_cp = state.checkpoint();
451                state.expect(Identifier_).ok(); // Column
452                state.finish_at(col_cp, SqlElementType::ColumnName);
453
454                state.expect(Equal).ok();
455                PrattParser::parse(state, 0, self);
456                state.finish_at(assign_cp, SqlElementType::Assignment);
457
458                if !state.eat(Comma) {
459                    break;
460                }
461            }
462        }
463
464        if state.eat(Where) {
465            PrattParser::parse(state, 0, self);
466        }
467
468        self.parse_returning_clause(state);
469
470        state.eat(Semicolon);
471        state.finish_at(cp, SqlElementType::UpdateStatement);
472        Ok(())
473    }
474
475    fn parse_delete<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
476        use crate::lexer::SqlTokenType::*;
477        let cp = state.checkpoint();
478        state.expect(Delete).ok();
479        state.eat(From);
480
481        let table_cp = state.checkpoint();
482        state.expect(Identifier_).ok(); // TableName
483        state.finish_at(table_cp, SqlElementType::TableName);
484
485        if state.eat(Where) {
486            PrattParser::parse(state, 0, self);
487        }
488
489        state.eat(Semicolon);
490        state.finish_at(cp, SqlElementType::DeleteStatement);
491        Ok(())
492    }
493
494    fn parse_create<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
495        use crate::lexer::SqlTokenType::*;
496        let cp = state.checkpoint();
497        state.expect(Create).ok();
498
499        if state.eat(Table) {
500            state.eat(If);
501            state.eat(Not);
502            state.eat(Exists);
503
504            let table_cp = state.checkpoint();
505            state.expect(Identifier_).ok(); // TableName
506            state.finish_at(table_cp, SqlElementType::TableName);
507
508            if state.eat(LeftParen) {
509                while state.not_at_end() && state.peek_kind() != Some(RightParen) {
510                    let col_cp = state.checkpoint();
511
512                    let name_cp = state.checkpoint();
513                    state.expect(Identifier_).ok(); // Column Name
514                    state.finish_at(name_cp, SqlElementType::ColumnName);
515
516                    // Type
517                    self.parse_data_type(state);
518
519                    // Constraints
520                    while state.not_at_end() && !matches!(state.peek_kind(), Some(Comma) | Some(RightParen)) {
521                        if state.eat(Primary) {
522                            state.expect(Key).ok();
523                        }
524                        else if state.eat(Not) {
525                            state.expect(Null).ok();
526                        }
527                        else if state.eat(Null) {
528                        }
529                        else if state.eat(Unique) {
530                        }
531                        else if state.eat(Default) {
532                            let expr_cp = state.checkpoint();
533                            PrattParser::parse(state, 0, self);
534                            state.finish_at(expr_cp, SqlElementType::Expression);
535                        }
536                        else if state.eat(Check) {
537                            if state.eat(LeftParen) {
538                                let expr_cp = state.checkpoint();
539                                PrattParser::parse(state, 0, self);
540                                state.finish_at(expr_cp, SqlElementType::Expression);
541                                state.expect(RightParen).ok();
542                            }
543                        }
544                        else if state.eat(AutoIncrement) {
545                        }
546                        else {
547                            state.bump();
548                        }
549                    }
550
551                    state.finish_at(col_cp, SqlElementType::ColumnDefinition);
552                    if !state.eat(Comma) {
553                        break;
554                    }
555                }
556                state.expect(RightParen).ok();
557            }
558        }
559        else if state.eat(View) {
560            let name_cp = state.checkpoint();
561            state.expect(Identifier_).ok();
562            state.finish_at(name_cp, SqlElementType::Identifier);
563
564            state.expect(As).ok();
565            self.parse_select(state)?;
566        }
567        else if state.peek_kind() == Some(Index) || state.peek_kind() == Some(Unique) {
568            state.eat(Unique);
569            if state.eat(Index) {
570                let name_cp = state.checkpoint();
571                state.expect(Identifier_).ok(); // Index Name
572                state.finish_at(name_cp, SqlElementType::Identifier);
573
574                state.expect(On).ok();
575
576                let table_cp = state.checkpoint();
577                state.expect(Identifier_).ok(); // Table Name
578                state.finish_at(table_cp, SqlElementType::TableName);
579
580                if state.eat(LeftParen) {
581                    while state.not_at_end() && state.peek_kind() != Some(RightParen) {
582                        let col_cp = state.checkpoint();
583                        state.expect(Identifier_).ok();
584                        state.finish_at(col_cp, SqlElementType::Identifier);
585                        state.eat(Comma);
586                    }
587                    state.expect(RightParen).ok();
588                }
589            }
590        }
591        else if state.eat(Database) || state.eat(Schema) {
592            state.eat(If);
593            state.eat(Not);
594            state.eat(Exists);
595            let name_cp = state.checkpoint();
596            state.expect(Identifier_).ok();
597            state.finish_at(name_cp, SqlElementType::Identifier);
598        }
599
600        state.eat(Semicolon);
601        state.finish_at(cp, SqlElementType::CreateStatement);
602        Ok(())
603    }
604
605    fn parse_drop<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
606        use crate::lexer::SqlTokenType::*;
607        let cp = state.checkpoint();
608        state.expect(Drop).ok();
609
610        if state.eat(Table) || state.eat(View) || state.eat(Index) || state.eat(Database) || state.eat(Schema) {
611            state.eat(If);
612            state.eat(Exists);
613            let table_cp = state.checkpoint();
614            state.expect(Identifier_).ok(); // Object Name
615            state.finish_at(table_cp, SqlElementType::TableName);
616        }
617
618        state.eat(Semicolon);
619        state.finish_at(cp, SqlElementType::DropStatement);
620        Ok(())
621    }
622
623    fn parse_alter<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) -> Result<(), OakError> {
624        use crate::lexer::SqlTokenType::*;
625        let cp = state.checkpoint();
626        state.expect(Alter).ok();
627
628        if state.eat(Table) {
629            let table_cp = state.checkpoint();
630            state.expect(Identifier_).ok(); // TableName
631            state.finish_at(table_cp, SqlElementType::TableName);
632
633            // Simplified ALTER TABLE actions
634            if state.peek_kind() == Some(Add) || state.peek_kind() == Some(Drop) || state.peek_kind() == Some(Rename) {
635                let action_cp = state.checkpoint();
636                if state.eat(Add) {
637                    state.eat(Column);
638                    state.expect(Identifier_).ok();
639                    // Optional data type
640                    self.parse_data_type(state);
641                }
642                else if state.eat(Drop) {
643                    state.eat(Column);
644                    state.expect(Identifier_).ok();
645                }
646                else if state.eat(Rename) {
647                    state.eat(To);
648                    state.expect(Identifier_).ok();
649                }
650                state.finish_at(action_cp, SqlElementType::AlterAction);
651            }
652        }
653
654        state.eat(Semicolon);
655        state.finish_at(cp, SqlElementType::AlterStatement);
656        Ok(())
657    }
658
659    fn parse_data_type<'a, S: Source + ?Sized>(&self, state: &mut State<'a, S>) {
660        use crate::lexer::SqlTokenType::*;
661        if state.not_at_end() && !matches!(state.peek_kind(), Some(Comma) | Some(RightParen) | Some(Primary) | Some(Not) | Some(Null) | Some(Unique) | Some(Default) | Some(Check) | Some(Foreign) | Some(References) | Some(Semicolon)) {
662            state.bump(); // Type name
663            if state.eat(LeftParen) {
664                state.expect(NumberLiteral).ok();
665                if state.eat(Comma) {
666                    state.expect(NumberLiteral).ok();
667                }
668                state.expect(RightParen).ok();
669            }
670        }
671    }
672}
673
674impl<'config> Parser<SqlLanguage> for SqlParser<'config> {
675    fn parse<'a, S: Source + ?Sized>(&self, text: &'a S, edits: &[TextEdit], cache: &'a mut impl ParseCache<SqlLanguage>) -> ParseOutput<'a, SqlLanguage> {
676        let lexer = crate::lexer::SqlLexer::new(&self.config);
677        parse_with_lexer(&lexer, text, edits, cache, |state| {
678            let cp = state.checkpoint();
679            while state.not_at_end() {
680                if state.current().map(|t| t.kind.is_ignored()).unwrap_or(false) {
681                    state.advance();
682                    continue;
683                }
684                self.parse_statement(state)?
685            }
686            Ok(state.finish_at(cp, SqlElementType::Root))
687        })
688    }
689}