1- // SPDX-FileCopyrightText: Copyright 2021-2024  Mark Rotteveel 
1+ // SPDX-FileCopyrightText: Copyright 2021-2025  Mark Rotteveel 
22// SPDX-License-Identifier: LGPL-2.1-or-later 
33package  org .firebirdsql .jaybird .parser ;
44
1414 * Detects the type of statement, and - optionally - whether a DML statement has a {@code RETURNING} clause. 
1515 * <p> 
1616 * If the detected statement type is {@code UPDATE}, {@code DELETE}, {@code INSERT}, {@code UPDATE OR INSERT} and 
17-  * {@code MERGE}, it identifies the affected table and - optionally - whether or not  a {@code RETURNING} clause is 
18-  * present  (delegated to a {@link ReturningClauseDetector}). 
17+  * {@code MERGE}, it identifies the affected table and - optionally - if  a {@code RETURNING} clause is present  
18+  * (delegated to a {@link ReturningClauseDetector}). 
1919 * </p> 
2020 * <p> 
2121 * The types of statements detected are informed by the needs of Jaybird, and may change between point releases. 
2727@ InternalApi 
2828public  final  class  StatementDetector  implements  TokenVisitor  {
2929
30-     // TODO Add schema support: identify (optional) schema 
31- 
3230    private  static  final  StateAfterStart  INITIAL_OTHER  =
3331            new  StateAfterStart (ParserState .OTHER , LocalStatementType .OTHER );
3432    private  static  final  Map <CharSequence , StateAfterStart > NEXT_AFTER_START ;
@@ -55,6 +53,7 @@ public final class StatementDetector implements TokenVisitor {
5553    private  final  boolean  detectReturning ;
5654    private  LocalStatementType  statementType  = LocalStatementType .UNKNOWN ;
5755    private  ParserState  parserState  = ParserState .START ;
56+     private  Token  schemaToken ;
5857    private  Token  tableNameToken ;
5958    private  ReturningClauseDetector  returningClauseDetector ;
6059
@@ -105,10 +104,13 @@ public void visitToken(Token token, VisitorRegistrar visitorRegistrar) {
105104        if  (parserState .isFinalState ()) {
106105            // We're not interested anymore 
107106            visitorRegistrar .removeVisitor (this );
108-         } else  if  (parserState  == ParserState .FIND_RETURNING ) {
109-             // We're not interested anymore 
110-             visitorRegistrar .removeVisitor (this );
111-             if  (detectReturning ) {
107+         } else  if  (parserState  == ParserState .FIND_RETURNING 
108+                 || parserState  == ParserState .FIND_SCHEMA_SEPARATOR_OR_RETURNING ) {
109+             if  (parserState  == ParserState .FIND_RETURNING ) {
110+                 // We're not interested anymore 
111+                 visitorRegistrar .removeVisitor (this );
112+             }
113+             if  (detectReturning  && returningClauseDetector  == null ) {
112114                // Use ReturningClauseDetector to handle detection 
113115                returningClauseDetector  = new  ReturningClauseDetector ();
114116                visitorRegistrar .addVisitor (returningClauseDetector );
@@ -124,8 +126,7 @@ public void complete(VisitorRegistrar visitorRegistrar) {
124126    }
125127
126128    public  StatementIdentification  toStatementIdentification () {
127-         return  new  StatementIdentification (statementType , tableNameToken  != null  ? tableNameToken .text () : null ,
128-                 returningClauseDetected ());
129+         return  new  StatementIdentification (statementType , schemaToken , tableNameToken , returningClauseDetected ());
129130    }
130131
131132    boolean  returningClauseDetected () {
@@ -139,6 +140,10 @@ public LocalStatementType getStatementType() {
139140        return  statementType ;
140141    }
141142
143+     Token  getSchemaToken () {
144+         return  schemaToken ;
145+     }
146+ 
142147    Token  getTableNameToken () {
143148        return  tableNameToken ;
144149    }
@@ -151,6 +156,10 @@ private void updateStatementType(LocalStatementType statementType) {
151156        }
152157    }
153158
159+     private  void  setSchemaToken (Token  schemaToken ) {
160+         this .schemaToken  = schemaToken ;
161+     }
162+ 
154163    private  void  setTableNameToken (Token  tableNameToken ) {
155164        this .tableNameToken  = tableNameToken ;
156165    }
@@ -213,12 +222,46 @@ ParserState next(Token token, StatementDetector detector) {
213222        },
214223        // Shared by UPDATE, DELETE and MERGE 
215224        DML_TARGET  {
225+             @ Override 
226+             ParserState  next (Token  token , StatementDetector  detector ) {
227+                 if  (token .isValidIdentifier ()) {
228+                     detector .setTableNameToken (token );
229+                     return  DML_SCHEMA_SEPARATOR_OR_POSSIBLE_ALIAS ;
230+                 }
231+                 return  forceOther (detector );
232+             }
233+         },
234+         // Shared by UPDATE, DELETE and MERGE 
235+         DML_SCHEMA_SEPARATOR_OR_POSSIBLE_ALIAS  {
236+             @ Override 
237+             ParserState  next (Token  token , StatementDetector  detector ) {
238+                 if  (token  instanceof  PeriodToken ) {
239+                     // What was detected as table, is actually the schema 
240+                     detector .setSchemaToken (detector .getTableNameToken ());
241+                     detector .setTableNameToken (null );
242+                     return  DML_SCHEMA_QUALIFIED_TABLE_NAME ;
243+                 } else  if  (token .isValidIdentifier ()) {
244+                     // either alias or possibly returning clause 
245+                     return  FIND_RETURNING ;
246+                 } else  if  (token  instanceof  ReservedToken ) {
247+                     if  (token .equalsIgnoreCase ("AS" )) {
248+                         return  DML_ALIAS ;
249+                     }
250+                     return  FIND_RETURNING ;
251+                 }
252+                 // Unexpected or invalid token at this point 
253+                 return  forceOther (detector );
254+             }
255+         },
256+         // Shared by UPDATE, DELETE and MERGE 
257+         DML_SCHEMA_QUALIFIED_TABLE_NAME  {
216258            @ Override 
217259            ParserState  next (Token  token , StatementDetector  detector ) {
218260                if  (token .isValidIdentifier ()) {
219261                    detector .setTableNameToken (token );
220262                    return  DML_POSSIBLE_ALIAS ;
221263                }
264+                 // Unexpected or invalid token at this point 
222265                return  forceOther (detector );
223266            }
224267        },
@@ -263,7 +306,7 @@ ParserState next(Token token, StatementDetector detector) {
263306            ParserState  next (Token  token , StatementDetector  detector ) {
264307                if  (token .isValidIdentifier ()) {
265308                    detector .setTableNameToken (token );
266-                     return  FIND_RETURNING ;
309+                     return  FIND_SCHEMA_SEPARATOR_OR_RETURNING ;
267310                }
268311                // Syntax error 
269312                return  forceOther (detector );
@@ -279,6 +322,29 @@ ParserState next(Token token, StatementDetector detector) {
279322                return  forceOther (detector );
280323            }
281324        },
325+         FIND_SCHEMA_SEPARATOR_OR_RETURNING  {
326+             @ Override 
327+             ParserState  next (Token  token , StatementDetector  detector ) {
328+                 if  (token  instanceof  PeriodToken ) {
329+                     detector .setSchemaToken (detector .getTableNameToken ());
330+                     detector .setTableNameToken (null );
331+                     return  FIND_SCHEMA_QUALIFIED_TABLE_OR_RETURNING ;
332+                 } else  {
333+                     return  FIND_RETURNING ;
334+                 }
335+             }
336+         },
337+         FIND_SCHEMA_QUALIFIED_TABLE_OR_RETURNING  {
338+             @ Override 
339+             ParserState  next (Token  token , StatementDetector  detector ) {
340+                 if  (token .isValidIdentifier ()) {
341+                     detector .setTableNameToken (token );
342+                     return  FIND_RETURNING ;
343+                 }
344+                 // Syntax error 
345+                 return  forceOther (detector );
346+             }
347+         },
282348        // finding itself is offloaded to ReturningClauseDetector 
283349        FIND_RETURNING ,
284350        COMMIT_ROLLBACK  {
0 commit comments