001package gudusoft.gsqlparser.stmt.mssql;
002
003import gudusoft.gsqlparser.*;
004import gudusoft.gsqlparser.nodes.TBlockSqlNode;
005import gudusoft.gsqlparser.nodes.TParseTreeVisitor;
006import gudusoft.gsqlparser.nodes.TStatementSqlNode;
007import gudusoft.gsqlparser.nodes.mssql.TTryCatchSqlNode;
008
009/**
010 * Represents SQL Server TRY...CATCH statement.
011 * <p>
012 * Syntax:
013 * <pre>
014 * BEGIN TRY
015 *     { sql_statement | statement_block }
016 * END TRY
017 * BEGIN CATCH
018 *     [ { sql_statement | statement_block } ]
019 * END CATCH
020 * </pre>
021 * <p>
022 * Example:
023 * <pre>
024 * BEGIN TRY
025 *     SELECT 1/0;
026 * END TRY
027 * BEGIN CATCH
028 *     SELECT ERROR_MESSAGE();
029 * END CATCH
030 * </pre>
031 */
032public class TMssqlTryCatch extends TCustomSqlStatement {
033
034    private TStatementList tryStatements = null;
035    private TStatementList catchStatements = null;
036
037    public TMssqlTryCatch(EDbVendor dbvendor) {
038        super(dbvendor);
039        sqlstatementtype = ESqlStatementType.sstmssqlTryCatch;
040    }
041
042    void buildsql() {
043    }
044
045    void clear() {
046    }
047
048    String getasprettytext() {
049        return "";
050    }
051
052    void iterate(TVisitorAbs pvisitor) {
053    }
054
055    /**
056     * Get the statements inside the BEGIN TRY ... END TRY block.
057     */
058    public TStatementList getTryStatements() {
059        if (tryStatements == null) {
060            tryStatements = new TStatementList();
061        }
062        return tryStatements;
063    }
064
065    /**
066     * Get the statements inside the BEGIN CATCH ... END CATCH block.
067     * May be empty if the CATCH block has no statements.
068     */
069    public TStatementList getCatchStatements() {
070        if (catchStatements == null) {
071            catchStatements = new TStatementList();
072        }
073        return catchStatements;
074    }
075
076    public int doParseStatement(TCustomSqlStatement psql) {
077        if (rootNode == null) return -1;
078        super.doParseStatement(psql);
079
080        // Handle TTryCatchSqlNode (from grammar with separate TRY/CATCH lists)
081        if (rootNode instanceof TTryCatchSqlNode) {
082            TTryCatchSqlNode tryCatchNode = (TTryCatchSqlNode) rootNode;
083
084            // Parse TRY block statements
085            if (tryCatchNode.getTryStmts() != null) {
086                tryCatchNode.getTryStmts().doParse(this, ESqlClause.unknown);
087                for (int i = 0; i < tryCatchNode.getTryStmts().size(); i++) {
088                    this.getTryStatements().add(tryCatchNode.getTryStmts().getStatementSqlNode(i).getStmt());
089                }
090            }
091
092            // Parse CATCH block statements
093            if (tryCatchNode.getCatchStmts() != null) {
094                tryCatchNode.getCatchStmts().doParse(this, ESqlClause.unknown);
095                for (int i = 0; i < tryCatchNode.getCatchStmts().size(); i++) {
096                    this.getCatchStatements().add(tryCatchNode.getCatchStmts().getStatementSqlNode(i).getStmt());
097                }
098            }
099        }
100        // Handle TBlockSqlNode (from token-based detection where grammar merges TRY+CATCH)
101        else if (rootNode instanceof TBlockSqlNode) {
102            TBlockSqlNode blockNode = (TBlockSqlNode) rootNode;
103
104            if (blockNode.getStmts() != null) {
105                blockNode.getStmts().doParse(this, ESqlClause.unknown);
106
107                // Find the position where TRY block ends and CATCH block begins
108                // by looking for END TRY tokens
109                int endTryPosition = findEndTryPosition();
110                int catchStartPosition = findCatchStartPosition();
111
112                for (int i = 0; i < blockNode.getStmts().size(); i++) {
113                    TStatementSqlNode stmtNode = blockNode.getStmts().getStatementSqlNode(i);
114                    TCustomSqlStatement stmt = stmtNode.getStmt();
115
116                    if (stmt != null) {
117                        // Determine if statement is in TRY or CATCH block based on token position
118                        int stmtStartPos = (stmt.getStartToken() != null) ? stmt.getStartToken().posinlist : -1;
119
120                        if (catchStartPosition > 0 && stmtStartPos >= catchStartPosition) {
121                            // Statement is after BEGIN CATCH, belongs to CATCH block
122                            this.getCatchStatements().add(stmt);
123                        } else if (endTryPosition > 0 && stmtStartPos < endTryPosition) {
124                            // Statement is before END TRY, belongs to TRY block
125                            this.getTryStatements().add(stmt);
126                        } else if (catchStartPosition <= 0) {
127                            // No CATCH block, all statements are in TRY
128                            this.getTryStatements().add(stmt);
129                        }
130                    }
131                }
132            }
133        }
134
135        return 0;
136    }
137
138    /**
139     * Find the token position of END TRY in the source.
140     */
141    private int findEndTryPosition() {
142        if (getStartToken() == null) return -1;
143        TSourceTokenList tokenList = getStartToken().container;
144        if (tokenList == null) return -1;
145
146        int startPos = getStartToken().posinlist;
147        int endPos = (getEndToken() != null) ? getEndToken().posinlist : tokenList.size();
148
149        for (int i = startPos; i < endPos; i++) {
150            TSourceToken token = tokenList.get(i);
151            if (token.astext != null && token.astext.equalsIgnoreCase("END")) {
152                // Check if next solid token is TRY
153                for (int j = i + 1; j < endPos; j++) {
154                    TSourceToken nextToken = tokenList.get(j);
155                    if (nextToken.tokentype != ETokenType.ttwhitespace &&
156                        nextToken.tokentype != ETokenType.ttreturn) {
157                        if (nextToken.astext != null && nextToken.astext.equalsIgnoreCase("TRY")) {
158                            return j; // Return position after END TRY
159                        }
160                        break;
161                    }
162                }
163            }
164        }
165        return -1;
166    }
167
168    /**
169     * Find the token position where BEGIN CATCH starts.
170     */
171    private int findCatchStartPosition() {
172        if (getStartToken() == null) return -1;
173        TSourceTokenList tokenList = getStartToken().container;
174        if (tokenList == null) return -1;
175
176        int startPos = getStartToken().posinlist;
177        int endPos = (getEndToken() != null) ? getEndToken().posinlist : tokenList.size();
178
179        for (int i = startPos; i < endPos; i++) {
180            TSourceToken token = tokenList.get(i);
181            if (token.astext != null && token.astext.equalsIgnoreCase("BEGIN")) {
182                // Check if next solid token is CATCH
183                for (int j = i + 1; j < endPos; j++) {
184                    TSourceToken nextToken = tokenList.get(j);
185                    if (nextToken.tokentype != ETokenType.ttwhitespace &&
186                        nextToken.tokentype != ETokenType.ttreturn) {
187                        if (nextToken.astext != null && nextToken.astext.equalsIgnoreCase("CATCH")) {
188                            return j; // Return position of BEGIN CATCH
189                        }
190                        break;
191                    }
192                }
193            }
194        }
195        return -1;
196    }
197
198    public void accept(TParseTreeVisitor v) {
199        v.preVisit(this);
200        v.postVisit(this);
201    }
202
203    public void acceptChildren(TParseTreeVisitor v) {
204        v.preVisit(this);
205        if (tryStatements != null && tryStatements.size() > 0) {
206            tryStatements.acceptChildren(v);
207        }
208        if (catchStatements != null && catchStatements.size() > 0) {
209            catchStatements.acceptChildren(v);
210        }
211        v.postVisit(this);
212    }
213
214    public void setTryStatements(TStatementList tryStatements) {
215        this.tryStatements = tryStatements;
216    }
217
218    public void setCatchStatements(TStatementList catchStatements) {
219        this.catchStatements = catchStatements;
220    }
221}