001/*
002 * Decompiled with CFR 0.152.
003 */
004package gudusoft.gsqlparser.parser;
005
006import gudusoft.gsqlparser.EDataType;
007import gudusoft.gsqlparser.EDbVendor;
008import gudusoft.gsqlparser.EErrorType;
009import gudusoft.gsqlparser.EFindSqlStateType;
010import gudusoft.gsqlparser.ESqlStatementType;
011import gudusoft.gsqlparser.ETokenType;
012import gudusoft.gsqlparser.TBaseType;
013import gudusoft.gsqlparser.TCustomLexer;
014import gudusoft.gsqlparser.TCustomParser;
015import gudusoft.gsqlparser.TCustomSqlStatement;
016import gudusoft.gsqlparser.TLexerTeradata;
017import gudusoft.gsqlparser.TParserTeradata;
018import gudusoft.gsqlparser.TSourceToken;
019import gudusoft.gsqlparser.TSourceTokenList;
020import gudusoft.gsqlparser.TStatementList;
021import gudusoft.gsqlparser.TSyntaxError;
022import gudusoft.gsqlparser.nodes.TDatatypeAttribute;
023import gudusoft.gsqlparser.nodes.TFunctionCall;
024import gudusoft.gsqlparser.nodes.TTypeName;
025import gudusoft.gsqlparser.nodes.teradata.TTeradataHelper;
026import gudusoft.gsqlparser.parser.AbstractSqlParser;
027import gudusoft.gsqlparser.parser.ParserContext;
028import gudusoft.gsqlparser.parser.SqlParseResult;
029import gudusoft.gsqlparser.resolver.TSQLResolver;
030import gudusoft.gsqlparser.sqlcmds.SqlCmdsFactory;
031import gudusoft.gsqlparser.stmt.TUnknownSqlStatement;
032import gudusoft.gsqlparser.stmt.teradata.TTeradataBTEQCmd;
033import gudusoft.gsqlparser.stmt.teradata.TTeradataFastExportCmd;
034import gudusoft.gsqlparser.stmt.teradata.TTeradataFastLoadCmd;
035import gudusoft.gsqlparser.stmt.teradata.TTeradataMultiLoadCmd;
036import gudusoft.gsqlparser.stmt.teradata.utilities.BteqCmdType;
037import java.util.ArrayList;
038
039public class TeradataSqlParser
040extends AbstractSqlParser {
041    public TLexerTeradata flexer;
042    private TParserTeradata fparser;
043    private TCustomSqlStatement gcurrentsqlstatement;
044    private char curdelimiterchar = (char)59;
045
046    // BTEQ state tracking - must persist across token processing
047    private boolean inBTEQComment = false;
048    private boolean isContinueBTEQCmd = false;
049    // Track when we're collecting a SQL statement after .IF/.ELSEIF THEN
050    private boolean isInIfThenStatement = false;
051
052    public TeradataSqlParser() {
053        super(EDbVendor.dbvteradata);
054        this.delimiterChar = '/';  // Teradata uses forward slash as delimiter for stored procedures
055        this.defaultDelimiterStr = ";";  // Match TGSqlParser default
056        this.flexer = new TLexerTeradata();
057        this.flexer.delimiterchar = '/';  // Must match TGSqlParser: delimiterchar = '/'
058        this.flexer.defaultDelimiterStr = ";";  // Match TGSqlParser default
059        this.lexer = this.flexer;
060        this.fparser = new TParserTeradata(null);
061        this.fparser.lexer = this.flexer;
062    }
063
064    @Override
065    protected TCustomLexer getLexer(ParserContext context) {
066        // Transfer Teradata utility type from context to lexer
067        if (context.getTeradataUtilityType() != null) {
068            this.flexer.setTeradataUtilityType(context.getTeradataUtilityType());
069        }
070        return this.flexer;
071    }
072
073    @Override
074    protected TCustomParser getParser(ParserContext context, TSourceTokenList tokens) {
075        this.fparser.sourcetokenlist = tokens;
076        return this.fparser;
077    }
078
079    @Override
080    protected TCustomParser getSecondaryParser(ParserContext context, TSourceTokenList tokens) {
081        return null;
082    }
083
084    @Override
085    protected void tokenizeVendorSql() {
086        this.doteradatatexttotokenlist();
087    }
088
089    @Override
090    protected void setupVendorParsersForExtraction() {
091        this.fparser.sqlcmds = this.sqlcmds;
092        this.fparser.sourcetokenlist = this.sourcetokenlist;
093    }
094
095    @Override
096    protected void extractVendorRawStatements(SqlParseResult.Builder builder) {
097        this.doteradatagetrawsqlstatements(builder);
098    }
099
100    private void doteradatatexttotokenlist() {
101        TSourceToken asourcetoken, lcprevst;
102        int yychar;
103
104        asourcetoken = this.getanewsourcetoken();
105        if (asourcetoken == null) {
106            return;
107        }
108        yychar = asourcetoken.tokencode;
109
110        while (yychar > 0) {
111            this.sourcetokenlist.add(asourcetoken);
112            asourcetoken = this.getanewsourcetoken();
113            if (asourcetoken == null) break;
114
115            if ((asourcetoken.tokencode == TBaseType.rrw_casespecific)
116                    || (asourcetoken.tokencode == TBaseType.rrw_teradata_cs)) {
117                // change not to not1 to make "not null column constraints" work correctly
118                // |RW_NOT1 RW_CASESPECIFIC
119                lcprevst = this.getprevsolidtoken(asourcetoken);
120                if (lcprevst != null) {
121                    if (lcprevst.tokencode == TBaseType.rrw_not) {
122                        lcprevst.tokencode = TBaseType.rw_not1;
123                    }
124                }
125            } else if (asourcetoken.tokencode == ';') {
126                lcprevst = this.getprevsolidtoken(asourcetoken);
127                if (lcprevst != null) {
128                    if (lcprevst.tokencode == ';') {
129                        asourcetoken.tokencode = TBaseType.sqlpluscmd;
130                    }
131                }
132            } else if (asourcetoken.tokencode == TBaseType.variable) { // mantisbt/view.php?id=972
133                if (asourcetoken.toString().toLowerCase().endsWith("begin")) { // MYPROC :BEGIN
134                    asourcetoken.tokencode = TBaseType.rrw_begin;
135                    asourcetoken.tokentype = ETokenType.ttkeyword;
136
137                    lcprevst = this.getprevsolidtoken(asourcetoken);
138                    if (lcprevst != null) {
139                        lcprevst.tokencode = TBaseType.mslabel;
140                    }
141                }
142            } else if (asourcetoken.tokencode == TCustomLexer.UNICODE_ENCODE_ID) {
143                if (asourcetoken.toString().endsWith("008D")) { // REVERSE LINE FEED, https://codepoints.net/U+008D?lang=en
144                    asourcetoken.tokencode = TBaseType.lexspace;
145                } else {
146                    asourcetoken.tokencode = TBaseType.ident;
147                }
148            }
149
150            yychar = asourcetoken.tokencode;
151        }
152    }
153
154    private TSourceToken getprevsolidtoken(TSourceToken ptoken) {
155        TSourceToken ret = null;
156        TSourceTokenList lctokenlist = ptoken.container;
157        if (lctokenlist != null && ptoken.posinlist > 0 && lctokenlist.size() > ptoken.posinlist - 1) {
158            ret = lctokenlist.get((int)(ptoken.posinlist - 1)).tokentype != ETokenType.ttwhitespace && lctokenlist.get((int)(ptoken.posinlist - 1)).tokentype != ETokenType.ttreturn && lctokenlist.get((int)(ptoken.posinlist - 1)).tokentype != ETokenType.ttsimplecomment && lctokenlist.get((int)(ptoken.posinlist - 1)).tokentype != ETokenType.ttbracketedcomment ? lctokenlist.get(ptoken.posinlist - 1) : lctokenlist.nextsolidtoken(ptoken.posinlist - 1, -1, false);
159        }
160        return ret;
161    }
162
163    private int doteradatagetrawsqlstatements(SqlParseResult.Builder builder) {
164        this.gcurrentsqlstatement = null;
165        EFindSqlStateType gst = EFindSqlStateType.stnormal;
166        int lcNestedParens = 0;
167        int lcNestedBeginEnd = 0;
168        int lcNestedCase = 0;
169        int lcNestedSample = 0;
170        Object lcprevst = null;
171        Object lcnextst = null;
172        Object lcnextst2 = null;
173        Object lcnextst3 = null;
174        TSourceToken lcprevsolidtoken = null;
175        TSourceToken ast = null;
176        int errorcount = 0;
177        // Note: inBTEQComment, isContinueBTEQCmd, isInIfThenStatement are instance fields
178        this.inBTEQComment = false;
179        this.isContinueBTEQCmd = false;
180        this.isInIfThenStatement = false;
181        char ch;
182        String lcstr;
183        block10: for (int i = 0; i < this.sourcetokenlist.size(); ++i) {
184            if (ast != null && ast.issolidtoken()) {
185                lcprevsolidtoken = ast;
186            }
187            ast = this.sourcetokenlist.get(i);
188            this.sourcetokenlist.curpos = i;
189            if (ast.tokencode != TBaseType.rrw_time && ast.tokencode != TBaseType.rrw_date && ast.tokencode != TBaseType.rrw_timestamp) {
190                if (ast.tokencode == '(') {
191                    this.handleLeftParenthesisToken(ast, i);
192                } else if (ast.tokencode == TBaseType.rrw_for) {
193                    this.handleForToken(ast);
194                } else if (ast.tokencode == TBaseType.rrw_teradata_last) {
195                    this.handleLastToken(ast);
196                } else if (ast.tokencode == TBaseType.rrw_teradata_pivot) {
197                    this.handlePivotToken(ast);
198                } else if (ast.tokencode == TBaseType.rrw_teradata_period) {
199                    this.handlePeriodToken(ast);
200                } else if (ast.tokencode == TBaseType.rrw_teradata_transaction) {
201                    this.handleTransactionToken(ast);
202                } else if (ast.tokencode == TBaseType.rrw_replace) {
203                    this.handleReplaceToken(ast);
204                } else if (ast.tokencode == TBaseType.rrw_between) {
205                    this.handleBetweenToken(ast);
206                } else if (ast.tokencode == TBaseType.rrw_declare) {
207                    this.handleDeclareToken(ast);
208                } else if (ast.tokencode == TBaseType.rrw_case && ast.tag == 0 && ast.posinlist + 1 < this.sourcetokenlist.size()) {
209                    this.handleCaseToken(ast);
210                } else if (ast.tokencode == TBaseType.rrw_grant) {
211                    this.handleGrantToken(ast);
212                } else if (ast.tokencode == TBaseType.variable) {
213                    this.handleVariableToken(ast);
214                } else if (ast.tokencode == TBaseType.rrw_teradata_sequenced || ast.tokencode == TBaseType.rrw_teradata_nonsequenced) {
215                    this.handleSequencedToken(ast);
216                }
217            }
218            switch (gst) {
219                case sterror: {
220                    if (ast.tokentype == ETokenType.ttsemicolon) {
221                        this.gcurrentsqlstatement.sourcetokenlist.add(ast);
222                        this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder);
223                        gst = EFindSqlStateType.stnormal;
224                        continue block10;
225                    }
226                    this.gcurrentsqlstatement.sourcetokenlist.add(ast);
227                    continue block10;
228                }
229                case stMultiLoadCmd: {
230                    gst = this.handleMultiLoadCmdState(ast, gst, builder);
231                    continue block10;
232                }
233                case stFastExportCmd: {
234                    gst = this.handleFastExportCmdState(ast, gst, builder);
235                    continue block10;
236                }
237                case stFastLoadCmd: {
238                    gst = this.handleFastLoadCmdState(ast, gst, builder);
239                    continue block10;
240                }
241                case stBTEQCmd: {
242                    gst = this.handleBTEQCmdState(ast, builder);
243                    continue block10;
244                }
245                case stnormal: {
246                    gst = this.handleNormalState(ast, gst, lcprevsolidtoken, builder);
247                    if (gst != EFindSqlStateType.ststoredprocedure) continue block10;
248                    lcNestedParens = 0;
249                    continue block10;
250                }
251                case stsql: {
252                    boolean readyToEnd = true;
253                    if (this.gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstteradatacreatemacro) {
254                        if (ast.tokencode == '(' || ast.tokencode == TBaseType.left_parenthesis_2) {
255                            ++lcNestedParens;
256                        }
257                        if (ast.tokencode == ')') {
258                            --lcNestedParens;
259                            if (lcNestedParens < 0) {
260                                lcNestedParens = 0;
261                            }
262                        }
263                        readyToEnd = lcNestedParens == 0;
264                    }
265                    if (ast.tokentype == ETokenType.ttsemicolon && readyToEnd) {
266                        this.gcurrentsqlstatement.sourcetokenlist.add(ast);
267                        this.gcurrentsqlstatement.semicolonended = ast;
268                        this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder);
269                        gst = EFindSqlStateType.stnormal;
270                        continue block10;
271                    }
272                    if (ast.tokencode == TBaseType.cmtdoublehyphen && ast.toString().trim().endsWith(TBaseType.sqlflow_stmt_delimiter_str)) {
273                        this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder);
274                        gst = EFindSqlStateType.stnormal;
275                        continue block10;
276                    }
277                    this.gcurrentsqlstatement.sourcetokenlist.add(ast);
278                    continue block10;
279                }
280                case ststoredprocedure: {
281                    boolean readyToEnd = true;
282                    if (ast.tokencode == ';') {
283                        // don't count case..end, sample ... end after ;
284                        lcNestedSample = 0;
285                        lcNestedCase = 0;
286                    } else if (ast.tokencode == TBaseType.rrw_case) { // case ... end
287                        ++lcNestedCase;
288                        TSourceToken prevst = ast.prevSolidToken();
289                        if (prevst != null && prevst.tokencode == TBaseType.rrw_end) {
290                            // don't count: end case
291                            --lcNestedCase;
292                        }
293                    } else if (ast.tokencode == TBaseType.rrw_teradata_sample) { // sample ... end
294                        ++lcNestedSample;
295                    }
296                    if (this.gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstcreatetrigger
297                            || this.gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstcreateprocedure
298                            || this.gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstcreatefunction) {
299                        if (ast.tokencode == '(' || ast.tokencode == TBaseType.left_parenthesis_2) {
300                            ++lcNestedParens;
301                        }
302                        if (ast.tokencode == ')' && --lcNestedParens < 0) {
303                            lcNestedParens = 0;
304                        }
305                        if (ast.tokencode == TBaseType.rrw_begin) {
306                            ++lcNestedBeginEnd;
307                        }
308                        if (ast.tokencode == TBaseType.rrw_end) {
309                            boolean countThisEnd = true;
310                            if (lcNestedSample > 0) {
311                                // don't count sample...end
312                                --lcNestedSample;
313                                countThisEnd = false;
314                            } else if (lcNestedCase > 0) {
315                                // don't count case...end
316                                --lcNestedCase;
317                                countThisEnd = false;
318                            }
319                            if (countThisEnd) {
320                                // don't count end while/if/case/loop/for/repeat
321                                TSourceToken nt = ast.nextSolidToken();
322                                if (nt != null) {
323                                    countThisEnd = nt.tokencode != TBaseType.rrw_while
324                                            && nt.tokencode != TBaseType.rrw_if
325                                            && nt.tokencode != TBaseType.rrw_case
326                                            && nt.tokencode != TBaseType.rrw_loop
327                                            && nt.tokencode != TBaseType.rrw_for
328                                            && nt.tokencode != TBaseType.rrw_repeat;
329                                }
330                            }
331                            if (countThisEnd && --lcNestedBeginEnd < 0) {
332                                lcNestedBeginEnd = 0;
333                            }
334                        }
335                        readyToEnd = lcNestedParens == 0 && lcNestedBeginEnd == 0;
336                    }
337                    // single stmt in function/procedure/trigger may use ; as terminate char
338                    // so default terminate char is ;, if begin is found, then
339                    // set terminate char to DelimiterChar
340                    if (this.curdelimiterchar != this.delimiterChar) {
341                        if (ast.tokencode == TBaseType.rrw_begin) {
342                            this.curdelimiterchar = this.delimiterChar;
343                        } else if (ast.tokencode == TBaseType.mslabel && ast.container.nextsolidtoken(ast, 1, false).tokencode == TBaseType.rrw_begin) {
344                            this.curdelimiterchar = this.delimiterChar;
345                        }
346                    }
347                    if (this.curdelimiterchar == ';') {
348                        this.gcurrentsqlstatement.sourcetokenlist.add(ast);
349                        if (ast.tokentype == ETokenType.ttsemicolon
350                                && (this.gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstcreatetrigger
351                                    || this.gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstcreateprocedure)
352                                && readyToEnd) {
353                            gst = EFindSqlStateType.stnormal;
354                            this.gcurrentsqlstatement.semicolonended = ast;
355                            this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder);
356                            continue block10;
357                        }
358                    } else {
359                        if (ast.getAstext().length() == 1) {
360                            ch = ast.getAstext().charAt(0);
361                        } else if (ast.getAstext().length() > 1) {
362                            ch = ast.getAstext().charAt(ast.getAstext().length() - 1);
363                        } else {
364                            ch = ' ';
365                        }
366                        if (ast.tokencode != TBaseType.cmtslashstar && ast.tokencode != TBaseType.cmtdoublehyphen
367                                && ch == this.curdelimiterchar && ast.isFirstTokenOfLine() && readyToEnd) {
368                            if (ast.getAstext().length() > 1) {
369                                lcstr = ast.getAstext().substring(0, ast.getAstext().length() - 1);
370                                int c2 = this.flexer.getkeywordvalue(lcstr);
371                                if (c2 > 0) {
372                                    ast.tokencode = c2;
373                                }
374                            } else {
375                                this.gcurrentsqlstatement.semicolonended = ast;
376                            }
377                            this.gcurrentsqlstatement.sourcetokenlist.add(ast);
378                            gst = EFindSqlStateType.stnormal;
379                            this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder);
380                        } else if (ast.searchToken(TBaseType.rrw_create, 1) != null && ast.searchToken(TBaseType.rrw_procedure, 4) != null && readyToEnd) {
381                            gst = EFindSqlStateType.stnormal;
382                            this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder);
383                        } else if (ast.tokencode == ';') {
384                            this.gcurrentsqlstatement.sourcetokenlist.add(ast);
385                            boolean startNewStatement = false;
386                            if (readyToEnd) {
387                                startNewStatement = this.sqlcmds.issql(ast.nextSolidToken(), gst, this.gcurrentsqlstatement) != null;
388                            }
389                            if (startNewStatement) {
390                                gst = EFindSqlStateType.stnormal;
391                                this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder);
392                            }
393                        } else {
394                            this.gcurrentsqlstatement.sourcetokenlist.add(ast);
395                        }
396                    }
397                    if (gst == EFindSqlStateType.ststoredprocedure && ast.tokencode == TBaseType.cmtdoublehyphen && ast.toString().trim().endsWith(TBaseType.sqlflow_stmt_delimiter_str)) {
398                        gst = EFindSqlStateType.stnormal;
399                        this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder);
400                    }
401                }
402            }
403        }
404        if (TBaseType.assigned(this.gcurrentsqlstatement) && (gst == EFindSqlStateType.stsql || gst == EFindSqlStateType.stBTEQCmd || gst == EFindSqlStateType.ststoredprocedure || gst == EFindSqlStateType.sterror
405                || gst == EFindSqlStateType.stFastExportCmd || gst == EFindSqlStateType.stFastLoadCmd || gst == EFindSqlStateType.stMultiLoadCmd)) {
406            this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, true, builder);
407        }
408        builder.sqlStatements(this.sqlstatements);
409        builder.syntaxErrors(this.syntaxErrors instanceof ArrayList ? (ArrayList)this.syntaxErrors : new ArrayList(this.syntaxErrors));
410        builder.errorCode(this.syntaxErrors.isEmpty() ? 0 : this.syntaxErrors.size());
411        return errorcount;
412    }
413
414    private void handleLeftParenthesisToken(TSourceToken ast, int i) {
415        TSourceToken lcnextst = this.sourcetokenlist.nextsolidtoken(i, 1, false);
416        TSourceToken lcprevst = this.getprevsolidtoken(ast);
417        if ((lcprevst != null) && (
418                (lcprevst.tokencode == TBaseType.rrw_teradata_cast)
419                || (lcprevst.tokencode == TBaseType.rrw_year) // sample sql: select year(date) - 1 mantisbt/view.php?id=3049
420        )) {
421            lcnextst = null; // don't check next token if prev token is cast. sample: cast(DATETIME as BIGINT)
422        }
423        if ((lcprevst != null) && (TFunctionCall.isBuiltIn(lcprevst.toString(), EDbVendor.dbvteradata))) {
424            if (((lcprevst.tokencode == TBaseType.rrw_end)
425                    || (lcprevst.tokencode == TBaseType.rrw_time)
426                    || (lcprevst.tokencode == TBaseType.rrw_date)
427                    || (lcprevst.tokencode == TBaseType.rrw_timestamp)
428                    || (lcprevst.tokencode == TBaseType.rrw_teradata_type)
429                    || (lcprevst.toString().equalsIgnoreCase("current_date"))
430                    || (lcprevst.toString().equalsIgnoreCase("current_time"))
431                    || (lcprevst.toString().equalsIgnoreCase("current_timestamp"))
432                    || (lcprevst.toString().equalsIgnoreCase("user"))
433                    || (lcprevst.toString().equalsIgnoreCase("current_user"))
434            )) {
435                // Keep lcnextst as is
436            } else {
437                lcnextst = null;
438            }
439        }
440        if (lcnextst != null) {
441            EDataType dataType = TTypeName.searchTeradataTypeByName(lcnextst.toString());
442            if ((dataType != null) && (lcprevst != null) && (lcprevst.tokencode != TBaseType.rrw_teradata_period)) {
443                ast.tokencode = TBaseType.rrw_teradata_start_data_conversion;
444                // lcnextst2 is the token 2 steps after (
445                TSourceToken lcnextst2 = this.sourcetokenlist.nextsolidtoken(i, 2, false);
446                // lcnextst3 is the token 3 steps after (
447                TSourceToken lcnextst3 = this.sourcetokenlist.nextsolidtoken(i, 3, false);
448                if (lcnextst2.tokencode == ',') {
449                    // this is data conversion: (date,format '$xxx'),
450                    // this is not data conversion: trunc(date,'yy')
451                    TSourceToken st1 = lcnextst3;
452                    TSourceToken st2 = this.sourcetokenlist.nextsolidtoken(i, 4, false);
453                    if (TDatatypeAttribute.searchDataTypeAttributeByName(st1, st2) == null) {
454                        // this is not data conversion, such as :trunc(date,'yy')
455                        ast.tokencode = '(';
456                    }
457                } else if (lcnextst2 != null) {
458                    // lcnextst3 may be null when (date) is at end of query
459                    this.handleDataTypeConversion(ast, dataType, lcnextst2, lcnextst3, lcprevst);
460                }
461            } else {
462                TSourceToken st1 = lcnextst;
463                TSourceToken st2 = this.sourcetokenlist.nextsolidtoken(i, 2, false);
464                if (TDatatypeAttribute.searchDataTypeAttributeByName(st1, st2) != null) {
465                    ast.tokencode = TBaseType.rrw_teradata_start_data_conversion;
466                }
467            }
468        }
469    }
470
471    private void handleDataTypeConversion(TSourceToken ast, EDataType dataType, TSourceToken lcnextst2, TSourceToken lcnextst3, TSourceToken lcprevst) {
472        switch (dataType) {
473            case date_t:
474            case time_t:
475                // next token is: ) or comma or data attribute token
476                if ((lcnextst2.tokencode == ')')
477                        || ((lcnextst2.tokencode == '(') && (lcnextst3 != null) && (lcnextst3.tokencode == TBaseType.iconst))) { // hire_date (time(6))
478                    // (date)
479                    // select (date) from t, here (date) is a function, not a data conversion,
480                    // But if prev token is identifier like TD_WEDNESDAY (no space before parenthesis), treat as function call
481                    // mantisbt/view.php?id=4023
482                    // f3 (date) - with space - is data conversion
483                    // TD_WEDNESDAY(date) - no space - is function call
484                    if ((lcprevst != null) && (lcprevst.tokencode == TBaseType.ident) && (lcnextst2.tokencode == ')')) {
485                        // Check if there's whitespace between identifier and '('
486                        // If identifier ends at column X and '(' starts at column X+1, there's no space
487                        long identEndCol = lcprevst.columnNo + lcprevst.toString().length();
488                        if (identEndCol == ast.columnNo) {
489                            // No space between identifier and '(' - treat as function call
490                            ast.tokencode = '(';
491                        }
492                        // If there's a space, keep as DATA_CONVERSION for data conversion pattern
493                    }
494                } else {
495                    ast.tokencode = '(';
496                }
497                break;
498            case timestamp_t:
499            case interval_t:
500                if (lcnextst2.tokencode == TBaseType.sconst) { // INSERT t1 (TIME '10:44:25.123-08:00',TIMESTAMP '2000-09-20 10:44:25.1234')
501                    ast.tokencode = '(';
502                }
503                break;
504            case period_t:
505                if (lcnextst2.tokencode == '(') {
506                    // 只有 period() 才确保这里 period 是datatype,否则period不是datatype
507                } else {
508                    ast.tokencode = '(';
509                }
510                break;
511            case char_t:
512                if (lcnextst2.tokencode == ')') {
513                    // NULL (CHAR) AS B
514                } else if ((lcnextst3 == null) || (lcnextst3.tokencode != TBaseType.iconst)) { // INSERT t1 (TIME '10:44:25.123-08:00',TIMESTAMP '2000-09-20 10:44:25.1234')
515                    ast.tokencode = '(';
516                }
517                break;
518            default:
519                break;
520        }
521    }
522
523    private void handleForToken(TSourceToken ast) {
524        TSourceToken nextToken = ast.searchTokenAfterObjectName();
525        if (nextToken != null) {
526            if (nextToken.tokencode == TBaseType.rrw_as) {
527                ast.tokencode = TBaseType.rrw_teradata_for_loop;
528            }
529        }
530    }
531
532    private void handleLastToken(TSourceToken ast) {
533        TSourceToken nextToken = ast.searchToken('(', 1);
534        if (nextToken != null) {
535            ast.tokencode = TBaseType.rrw_last_function;
536        }
537    }
538
539    private void handlePivotToken(TSourceToken ast) {
540        TSourceToken nextToken = ast.searchToken('(', 1);
541        if (nextToken == null) {
542            ast.tokencode = TBaseType.ident;
543        }
544    }
545
546    private void handlePeriodToken(TSourceToken ast) {
547        TSourceToken nextToken = ast.nextSolidToken();
548        if (nextToken == null) {
549            ast.tokencode = TBaseType.ident;
550        } else if (nextToken.tokencode == '(') {
551            // keep it as keyword
552        } else if (nextToken.tokencode == TBaseType.rrw_for) {
553            // keep it as keyword
554        } else if (nextToken.tokencode == TBaseType.sconst) {
555            // sample: DEFAULT PERIOD '(2005-02-03, 2006-02-03)'
556            // keep it as keyword
557        } else {
558            if (ast.prevSolidToken() == null) {
559                ast.tokencode = TBaseType.ident;
560            } else {
561                if (!ast.prevSolidToken().toString().equalsIgnoreCase("anchor")) {
562                    ast.tokencode = TBaseType.ident;
563                }
564            }
565        }
566    }
567
568    private void handleTransactionToken(TSourceToken ast) {
569        TSourceToken lcprevst = this.getprevsolidtoken(ast);
570        if (lcprevst != null) {
571            if (lcprevst.tokencode == TBaseType.rrw_end) {
572                lcprevst.tokencode = TBaseType.rrw_teradata_end_t;
573            }
574        }
575    }
576
577    private void handleReplaceToken(TSourceToken ast) {
578        TSourceToken nextToken = ast.searchToken('(', 1);
579        if (nextToken != null) {
580            ast.tokencode = TBaseType.ident;
581        }
582    }
583
584    private void handleBetweenToken(TSourceToken ast) {
585        TSourceToken nextToken = ast.nextSolidToken();
586        if (nextToken != null) {
587            if ((nextToken.tokencode == TBaseType.rrw_begin) || (nextToken.tokencode == TBaseType.rrw_end)) {
588                // BETWEEN begin(DODD_EFF_PERI)
589                nextToken.tokencode = TBaseType.ident;
590            }
591        }
592    }
593
594    private void handleDeclareToken(TSourceToken ast) {
595        TSourceToken nextToken = ast.nextSolidToken();
596        if (nextToken != null) {
597            TSourceToken nextnextToken = nextToken.nextSolidToken();
598            if (nextnextToken != null) {
599                if ((nextnextToken.toString().equalsIgnoreCase("cursor"))
600                        || (nextnextToken.toString().equalsIgnoreCase("insensitive"))
601                        || (nextnextToken.toString().equalsIgnoreCase("scroll"))
602                        || (nextnextToken.toString().equalsIgnoreCase("no"))) {
603                    nextToken.tokencode = TBaseType.rrw_teradata_cursor_name;
604                } else if ((nextnextToken.toString().equalsIgnoreCase("condition"))) {
605                    nextToken.tokencode = TBaseType.rrw_teradata_condition_name;
606                }
607            }
608        }
609    }
610
611    private void handleCaseToken(TSourceToken ast) {
612        int lcNest = 1;
613        for (int m = ast.posinlist + 1; m < this.sourcetokenlist.size() - 1; m++) {
614            TSourceToken st = this.sourcetokenlist.get(m);
615            if (st.tokencode == TBaseType.rrw_case) {
616                st.tag = 1; // this is nested case keyword, don't check this token later
617                lcNest++;
618            }
619            if (st.tokencode == TBaseType.rrw_end) {
620                lcNest--;
621                if (lcNest == 0) {
622                    TSourceToken caseToken = st.nextSolidToken();
623                    if (caseToken != null) {
624                        if (caseToken.tokencode == TBaseType.rrw_case) {
625                            ast.tokencode = TBaseType.rrw_teradata_case_stmt;
626                            caseToken.tag = 1; // this is case after end, don't check this token later
627                        }
628                    }
629                    // Always break after finding the matching END, whether it's followed by CASE or not
630                    // This prevents CASE expressions from incorrectly matching with later "END CASE" constructs
631                    break;
632                }
633            }
634        }
635    }
636
637    private void handleGrantToken(TSourceToken ast) {
638        TSourceToken prevSolidToken = ast.prevSolidToken();
639        if (prevSolidToken != null) {
640            if (prevSolidToken.tokencode == TBaseType.rrw_with) {
641                prevSolidToken.tokencode = TBaseType.rrw_teradata_with_grant;
642            }
643        }
644    }
645
646    private void handleVariableToken(TSourceToken ast) {
647        // USING ( _spVV8 VARCHAR(1024) CHARACTER SET UNICODE ) SELECT INTO :_spVV8 ;
648        // variable after into keyword treat as identifier
649        TSourceToken prevSolidToken = ast.prevSolidToken();
650        if (prevSolidToken != null) {
651            if (prevSolidToken.tokencode == TBaseType.rrw_into) {
652                ast.tokencode = TBaseType.ident;
653            }
654        }
655    }
656
657    private void handleSequencedToken(TSourceToken ast) {
658        TSourceToken nextSolidToken = ast.nextSolidToken();
659        if (nextSolidToken != null) {
660            if ((nextSolidToken.tokencode == TBaseType.rrw_teradata_validtime)) {
661                TSourceToken nextToken2 = nextSolidToken.nextSolidToken();
662                if (nextToken2 != null) {
663                    if ((nextToken2.tokencode == TBaseType.rrw_select)
664                            || (nextToken2.tokencode == TBaseType.rrw_insert)
665                            || (nextToken2.tokencode == TBaseType.rrw_update)
666                            || (nextToken2.tokencode == TBaseType.rrw_delete)
667                            || (nextToken2.tokencode == TBaseType.rrw_merge)) {
668                        // SEQUENCED VALIDTIME select/insert/update/delete/merge
669                        ast.tokencode = TBaseType.lexspace;
670                        nextSolidToken.tokencode = TBaseType.lexspace;
671                    }
672                }
673            }
674        }
675    }
676
677    private EFindSqlStateType handleMultiLoadCmdState(TSourceToken ast, EFindSqlStateType gst, SqlParseResult.Builder builder) {
678        this.gcurrentsqlstatement.sourcetokenlist.add(ast);
679        if ((ast.tokencode == ';') && (ast.isLastTokenOfLine())) {
680            TSourceToken nextToken = ast.nextSolidToken();
681            TSourceToken nextToken2 = nextToken != null ? nextToken.nextSolidToken() : null;
682            if (nextToken != null && nextToken2 != null &&
683                    nextToken.tokencode == '.' &&
684                    nextToken2.toString().equalsIgnoreCase("FIELD") &&
685                    nextToken.isFirstTokenOfLine()) {
686                // Remain in stMultiLoadCmd state
687            } else {
688                this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder);
689                gst = EFindSqlStateType.stnormal;
690            }
691        } else if (ast.isLastTokenOfLine()) {
692            // .IF ERRORCODE <> 0 THEN
693            // INSERT INTO Sou_EMP_Tab
694            //        (1, 'bala');
695            String cmdstr = this.gcurrentsqlstatement.sourcetokenlist.get(0).toString();
696            if (this.gcurrentsqlstatement.sourcetokenlist.size() > 1) {
697                cmdstr = cmdstr + this.gcurrentsqlstatement.sourcetokenlist.get(1).toString();
698            }
699            if ((cmdstr.toLowerCase().startsWith(".if")) || (cmdstr.toLowerCase().startsWith(".set")) || (cmdstr.toLowerCase().startsWith("set"))) {
700                this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder);
701                gst = EFindSqlStateType.stnormal;
702            }
703        }
704        return gst;
705    }
706
707    private EFindSqlStateType handleFastExportCmdState(TSourceToken ast, EFindSqlStateType gst, SqlParseResult.Builder builder) {
708        this.gcurrentsqlstatement.sourcetokenlist.add(ast);
709        if ((ast.tokencode == ';') && (ast.isLastTokenOfLine())) {
710            this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder);
711            gst = EFindSqlStateType.stnormal;
712        } else {
713            boolean findSQL = false;
714            TSourceToken nextst = ast.nextToken();
715            if (nextst != null) {
716                if (nextst.tokentype == ETokenType.ttreturn) {
717                    findSQL = (this.sqlcmds.issql(ast.nextSolidToken(), gst, this.gcurrentsqlstatement) != null);
718                    if (findSQL) {
719                        this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder);
720                        gst = EFindSqlStateType.stnormal;
721                    }
722                }
723            }
724        }
725        return gst;
726    }
727
728    private EFindSqlStateType handleFastLoadCmdState(TSourceToken ast, EFindSqlStateType gst, SqlParseResult.Builder builder) {
729        this.gcurrentsqlstatement.sourcetokenlist.add(ast);
730        if ((ast.tokencode == ';') && (ast.isLastTokenOfLine())) {
731            this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder);
732            gst = EFindSqlStateType.stnormal;
733        } else if (ast.tokencode == TBaseType.lexnewline) {
734            TSourceToken nextst = ast.nextSolidToken();
735            if (nextst != null) {
736                if (nextst.tokencode == TBaseType.BTEQCMD) {
737                    this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder);
738                    gst = EFindSqlStateType.stFastLoadCmd;
739                } else if (nextst.tokencode == '.') {
740                    this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder);
741                    gst = EFindSqlStateType.stFastLoadCmd;
742                } else if (this.sqlcmds.issql(nextst, gst, this.gcurrentsqlstatement) != null) {
743                    this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder);
744                    gst = EFindSqlStateType.stnormal;
745                }
746            }
747        }
748        return gst;
749    }
750
751    private EFindSqlStateType handleBTEQCmdState(TSourceToken ast, SqlParseResult.Builder builder) {
752        EFindSqlStateType gst = EFindSqlStateType.stBTEQCmd;
753        this.gcurrentsqlstatement.sourcetokenlist.add(ast);
754
755        if (ast.tokencode == TBaseType.lexnewline) {
756            if (this.isContinueBTEQCmd) {
757                this.isContinueBTEQCmd = false;
758            } else if (this.isInIfThenStatement) {
759                // Continue collecting tokens for .IF/.ELSEIF THEN until semicolon
760                // Don't complete the statement on newline
761            } else {
762                if (((TTeradataBTEQCmd)this.gcurrentsqlstatement).getBteqCmdType() == BteqCmdType.LOGON) {
763                    boolean findSQL = (this.sqlcmds.issql(ast.nextSolidToken(), gst, this.gcurrentsqlstatement) != null);
764                    if (findSQL) {
765                        this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder);
766                        gst = EFindSqlStateType.stnormal;
767                    }
768                } else {
769                    this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder);
770                    gst = EFindSqlStateType.stnormal;
771                }
772            }
773        } else if ((ast.tokencode == '-') && (ast.isLastTokenOfLine())) {
774            if (!this.inBTEQComment) {
775                this.isContinueBTEQCmd = true;
776            }
777        } else if (ast.tokencode == TBaseType.lexspace) {
778            // Do nothing
779        } else if (ast.tokencode == ';') {
780            // Reset IF THEN state when statement ends
781            this.isInIfThenStatement = false;
782            this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder);
783            boolean findNewCmdInSameLine = this.checkForNewBTEQCmdInSameLine(ast);
784            if (findNewCmdInSameLine) {
785                gst = EFindSqlStateType.stBTEQCmd;
786            } else {
787                gst = EFindSqlStateType.stnormal;
788            }
789        } else if (ast.tokencode == '*') {
790            this.isContinueBTEQCmd = false;
791        } else { // solid token
792            // Check if this is a THEN keyword at end of line for .IF/.ELSEIF commands
793            // In this case, the BTEQ command should continue to include the following SQL statement
794            BteqCmdType cmdType = ((TTeradataBTEQCmd)this.gcurrentsqlstatement).getBteqCmdType();
795            if ((cmdType == BteqCmdType.IF || cmdType == BteqCmdType.ELSEIF)
796                && ast.tokencode == TBaseType.rrw_then
797                && ast.isLastTokenOfLine()) {
798                this.isContinueBTEQCmd = true;
799                this.isInIfThenStatement = true;
800            } else {
801                this.isContinueBTEQCmd = false;
802            }
803        }
804        return gst;
805    }
806
807    private boolean checkForNewBTEQCmdInSameLine(TSourceToken ast) {
808        boolean findNewCmdInSameLine = false;
809        if (!ast.isLastTokenOfLine()) {
810            // check if next cmd like below:
811            // .SET SIDETITLES ON; .SET FOLDLINE ON ALL
812            TSourceToken nextst = ast.nextSolidToken();
813            if ((nextst != null) && (nextst.tokencode == '.')) {
814                TSourceToken nextst2 = nextst.nextSolidToken();
815                TSourceToken searchToken = null;
816                if (nextst2 != null) {
817                    if (nextst2.tokencode == TBaseType.rrw_set) {
818                        TSourceToken nextst3 = nextst2.nextSolidToken();
819                        if (nextst3 != null) {
820                            searchToken = nextst3;
821                        }
822                    } else {
823                        searchToken = nextst2;
824                    }
825
826                    BteqCmdType bteqCmdType = BteqCmdType.searchBteqCmd(searchToken);
827                    if (bteqCmdType != null) {
828                        this.gcurrentsqlstatement = new TTeradataBTEQCmd(this.vendor);
829                        ((TTeradataBTEQCmd)this.gcurrentsqlstatement).setBteqCmdType(bteqCmdType);
830                        this.gcurrentsqlstatement.sourcetokenlist.add(ast);
831                        findNewCmdInSameLine = true;
832                    }
833                }
834            }
835        }
836        return findNewCmdInSameLine;
837    }
838
839    private EFindSqlStateType handleNormalState(TSourceToken ast, EFindSqlStateType gst, TSourceToken lcprevsolidtoken, SqlParseResult.Builder builder) {
840        if ((ast.tokencode == TBaseType.cmtdoublehyphen)
841                || (ast.tokencode == TBaseType.cmtslashstar)
842                || (ast.tokencode == TBaseType.lexspace)
843                || (ast.tokencode == TBaseType.lexnewline)
844                || (ast.tokentype == ETokenType.ttsemicolon)) {
845            if (TBaseType.assigned(this.gcurrentsqlstatement)) {
846                this.gcurrentsqlstatement.sourcetokenlist.add(ast);
847            }
848
849            if ((lcprevsolidtoken != null) && (ast.tokentype == ETokenType.ttsemicolon)) {
850                if (lcprevsolidtoken.tokentype == ETokenType.ttsemicolon) {
851                    // ;;;; continuous semicolon, treat it as comment
852                    ast.tokentype = ETokenType.ttsimplecomment;
853                    ast.tokencode = TBaseType.cmtdoublehyphen;
854                }
855            }
856
857            return gst;
858        }
859        if ((gst = this.checkForBTEQOrUtilityCommand(ast, gst)) != EFindSqlStateType.stnormal) {
860            return gst;
861        }
862
863        // find a tokentext to start sql or stored procedure mode
864        this.gcurrentsqlstatement = this.sqlcmds.issql(ast, gst, this.gcurrentsqlstatement);
865
866        if (TBaseType.assigned(this.gcurrentsqlstatement)) {
867            ESqlStatementType[] ses = {ESqlStatementType.sstcreateprocedure, ESqlStatementType.sstteradatacreatefunction, ESqlStatementType.sstcreatetrigger};
868            if (this.includesqlstatementtype(this.gcurrentsqlstatement.sqlstatementtype, ses)) {
869                gst = EFindSqlStateType.ststoredprocedure;
870                this.gcurrentsqlstatement.sourcetokenlist.add(ast);
871                if ((this.gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstcreateprocedure)
872                        || (this.gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstteradatacreatefunction)
873                        || (this.gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstcreatetrigger)) {
874                    this.curdelimiterchar = ';';
875                }
876            } else {
877                gst = EFindSqlStateType.stsql;
878                this.gcurrentsqlstatement.sourcetokenlist.add(ast);
879            }
880        }
881
882        if ((!TBaseType.assigned(this.gcurrentsqlstatement)) && (ast.isFirstTokenOfLine())) {
883            gst = this.flexer.cmdType(ast);
884            if ((gst == EFindSqlStateType.stMultiLoadCmd) || (gst == EFindSqlStateType.stFastLoadCmd) || (gst == EFindSqlStateType.stFastExportCmd)) {
885                this.gcurrentsqlstatement = new TTeradataBTEQCmd(this.vendor);
886                ((TTeradataBTEQCmd)this.gcurrentsqlstatement).setFindSqlStateType(gst);
887                this.gcurrentsqlstatement.sourcetokenlist.add(ast);
888                return gst;
889            }
890            gst = EFindSqlStateType.stnormal;
891        }
892
893        if (!TBaseType.assigned(this.gcurrentsqlstatement)) { // error tokentext found
894            this.syntaxErrors.add(new TSyntaxError(ast.getAstext(), ast.lineNo, (ast.columnNo < 0 ? 0 : ast.columnNo),
895                    "Error when tokenlize", EErrorType.spwarning, TBaseType.MSG_WARNING_ERROR_WHEN_TOKENIZE, null, ast.posinlist));
896
897            ast.tokentype = ETokenType.tttokenlizererrortoken;
898            gst = EFindSqlStateType.sterror;
899
900            this.gcurrentsqlstatement = new TUnknownSqlStatement(this.vendor);
901            this.gcurrentsqlstatement.sqlstatementtype = ESqlStatementType.sstinvalid;
902            this.gcurrentsqlstatement.sourcetokenlist.add(ast);
903        }
904        return gst;
905    }
906
907    private EFindSqlStateType checkForBTEQOrUtilityCommand(TSourceToken ast, EFindSqlStateType gst) {
908        // Check for .bteq commands: either "." at start of line, or "." after ";" at start of line
909        boolean isDotAtStartOfLine = (ast.tokencode == '.') && (ast.isFirstTokenOfLine());
910        boolean isDotAfterSemicolonAtStartOfLine = false;
911        if ((ast.tokencode == '.') && !isDotAtStartOfLine) {
912            TSourceToken prevToken = ast.prevSolidToken();
913            if ((prevToken != null) && (prevToken.tokencode == ';') && (prevToken.isFirstTokenOfLine())) {
914                isDotAfterSemicolonAtStartOfLine = true;
915            }
916        }
917
918        if (isDotAtStartOfLine || isDotAfterSemicolonAtStartOfLine) {
919            // .bteq commands
920            TSourceToken nextst = ast.nextSolidToken();
921            if (nextst != null) {
922                gst = this.flexer.cmdType(nextst);
923                boolean foundCmd = false;
924                switch (gst) {
925                    case stMultiLoadCmd:
926                        this.gcurrentsqlstatement = new TTeradataMultiLoadCmd(this.vendor);
927                        gst = EFindSqlStateType.stMultiLoadCmd;
928                        ((TTeradataMultiLoadCmd)this.gcurrentsqlstatement).setCmdType(TTeradataHelper.searchMultiLoadTypeByName(nextst.getAstext()));
929                        foundCmd = true;
930                        break;
931                    case stFastExportCmd:
932                        this.gcurrentsqlstatement = new TTeradataFastExportCmd(this.vendor);
933                        gst = EFindSqlStateType.stFastExportCmd;
934                        foundCmd = true;
935                        break;
936                    case stFastLoadCmd:
937                        this.gcurrentsqlstatement = new TTeradataFastLoadCmd(this.vendor);
938                        gst = EFindSqlStateType.stFastLoadCmd;
939                        foundCmd = true;
940                        break;
941                    case stBTEQCmd:
942                        this.gcurrentsqlstatement = new TTeradataBTEQCmd(this.vendor);
943                        ((TTeradataBTEQCmd)this.gcurrentsqlstatement).setBteqCmdType(BteqCmdType.searchBteqCmd(nextst));
944                        gst = EFindSqlStateType.stBTEQCmd;
945                        foundCmd = true;
946                        break;
947                    default:
948                        break;
949                }
950                if (foundCmd) {
951                    this.gcurrentsqlstatement.sourcetokenlist.add(ast);
952                    return gst;
953                }
954            }
955        } else if ((ast.tokencode == '*') && (ast.isFirstTokenOfLine())) {
956            // BTEQ comment: * comment
957            gst = EFindSqlStateType.stBTEQCmd;
958            this.inBTEQComment = true;  // Mark that we're in a BTEQ comment
959            this.gcurrentsqlstatement = new TTeradataBTEQCmd(this.vendor);
960            ((TTeradataBTEQCmd)this.gcurrentsqlstatement).setBteqCmdType(BteqCmdType.COMMENT);
961            this.gcurrentsqlstatement.sourcetokenlist.add(ast);
962            return gst;
963        } else if ((ast.tokencode == '=') && (ast.isFirstTokenOfLine())) {
964            // BTEQ command starting with =
965            TSourceToken st1 = ast.prevSolidToken();
966            if ((st1 != null) && (st1.tokencode == ';')) {
967                gst = EFindSqlStateType.stBTEQCmd;
968                this.gcurrentsqlstatement = new TTeradataBTEQCmd(this.vendor);
969                ((TTeradataBTEQCmd)this.gcurrentsqlstatement).setFindSqlStateType(gst);
970                this.gcurrentsqlstatement.sourcetokenlist.add(ast);
971                return gst;
972            }
973        } else if ((ast.isFirstTokenOfLine()) && (BteqCmdType.searchBteqCmd(ast) != null)) {
974            TSourceToken st1 = ast.prevSolidToken();
975            if ((st1 != null) && (st1.tokencode == ';')) {
976                gst = EFindSqlStateType.stBTEQCmd;
977                this.gcurrentsqlstatement = new TTeradataBTEQCmd(this.vendor);
978                ((TTeradataBTEQCmd)this.gcurrentsqlstatement).setFindSqlStateType(gst);
979                this.gcurrentsqlstatement.sourcetokenlist.add(ast);
980                return gst;
981            }
982        } else if ((ast.tokencode == '&') && (ast.isFirstTokenOfLine())) {
983            // Teradata macro/substitution variable: &VARIABLE_NAME at start of line
984            // Treat it as a BTEQ-like command that ends at end of line
985            TSourceToken nextst = ast.nextSolidToken();
986            if (nextst != null && (nextst.tokencode == TBaseType.ident || nextst.tokentype == ETokenType.ttidentifier)) {
987                gst = EFindSqlStateType.stBTEQCmd;
988                this.gcurrentsqlstatement = new TTeradataBTEQCmd(this.vendor);
989                ((TTeradataBTEQCmd)this.gcurrentsqlstatement).setFindSqlStateType(gst);
990                this.gcurrentsqlstatement.sourcetokenlist.add(ast);
991                return gst;
992            }
993        }
994        return EFindSqlStateType.stnormal;
995    }
996
997
998    private boolean includesqlstatementtype(ESqlStatementType type, ESqlStatementType[] types) {
999        for (ESqlStatementType t : types) {
1000            if (type != t) continue;
1001            return true;
1002        }
1003        return false;
1004    }
1005
1006    @Override
1007    protected TStatementList performParsing(ParserContext context, TCustomParser parser, TCustomParser secondaryParser, TSourceTokenList tokens, TStatementList rawStatements) {
1008        this.fparser = (TParserTeradata)parser;
1009        this.sourcetokenlist = tokens;
1010        this.parserContext = context;
1011        this.sqlstatements = rawStatements;
1012        if (this.sqlstatements == null) {
1013            this.sqlstatements = new TStatementList();
1014            return this.sqlstatements;
1015        }
1016        if (this.sqlcmds == null) {
1017            this.sqlcmds = SqlCmdsFactory.get(this.vendor);
1018        }
1019        this.fparser.sqlcmds = this.sqlcmds;
1020
1021        // Initialize global context for semantic analysis
1022        // CRITICAL: When delegated from TGSqlParser, use TGSqlParser's frameStack
1023        // so that variables set in SET statements can be found by EXECUTE statements
1024        if (context != null && context.getGsqlparser() != null) {
1025            gudusoft.gsqlparser.TGSqlParser gsqlparser = (gudusoft.gsqlparser.TGSqlParser) context.getGsqlparser();
1026            this.frameStack = gsqlparser.getFrameStack();
1027
1028            // CRITICAL: Set gsqlparser on the NodeFactory - matches TGSqlParser behavior
1029            // This is needed for proper AST node creation during parsing
1030            this.fparser.getNf().setGsqlParser(gsqlparser);
1031
1032            // Create global context if needed
1033            this.globalContext = new gudusoft.gsqlparser.compiler.TContext();
1034            this.sqlEnv = new gudusoft.gsqlparser.sqlenv.TSQLEnv(this.vendor) {
1035                @Override
1036                public void initSQLEnv() {
1037                }
1038            };
1039            this.globalContext.setSqlEnv(this.sqlEnv, this.sqlstatements);
1040        } else {
1041            this.initializeGlobalContext();
1042        }
1043
1044        for (int i = 0; i < this.sqlstatements.size(); ++i) {
1045            TCustomSqlStatement stmt = this.sqlstatements.getRawSql(i);
1046            try {
1047                stmt.setFrameStack(this.frameStack);
1048                int parseResult = stmt.parsestatement(null, false, context.isOnlyNeedRawParseTree());
1049                boolean doRecover = TBaseType.ENABLE_ERROR_RECOVER_IN_CREATE_TABLE;
1050                if (doRecover && (parseResult != 0 || stmt.getErrorCount() > 0)) {
1051                    this.handleCreateTableErrorRecovery(stmt);
1052                }
1053                if (parseResult == 0 && stmt.getErrorCount() <= 0) continue;
1054                this.copyErrorsFromStatement(stmt);
1055                continue;
1056            }
1057            catch (Exception ex) {
1058                ex.printStackTrace();
1059                this.handleStatementParsingException(stmt, i, ex);
1060            }
1061        }
1062        if (this.globalFrame != null) {
1063            this.globalFrame.popMeFromStack(this.frameStack);
1064        }
1065        return this.sqlstatements;
1066    }
1067
1068    private void handleCreateTableErrorRecovery(TCustomSqlStatement stmt) {
1069        if (!(stmt.sqlstatementtype != ESqlStatementType.sstcreatetable && stmt.sqlstatementtype != ESqlStatementType.sstcreateindex || TBaseType.c_createTableStrictParsing)) {
1070            int nested = 0;
1071            boolean isIgnore = false;
1072            boolean isFoundIgnoreToken = false;
1073            TSourceToken firstIgnoreToken = null;
1074            for (int k = 0; k < stmt.sourcetokenlist.size(); ++k) {
1075                TSourceToken st = stmt.sourcetokenlist.get(k);
1076                if (isIgnore) {
1077                    if (st.issolidtoken() && st.tokencode != 59) {
1078                        isFoundIgnoreToken = true;
1079                        if (firstIgnoreToken == null) {
1080                            firstIgnoreToken = st;
1081                        }
1082                    }
1083                    if (st.tokencode == 59) continue;
1084                    st.tokencode = 273;
1085                    continue;
1086                }
1087                if (st.tokencode == 41 && --nested == 0) {
1088                    isIgnore = true;
1089                }
1090                if (st.tokencode != 40) continue;
1091                ++nested;
1092            }
1093            if (isFoundIgnoreToken) {
1094                stmt.parsestatement(null, false, this.parserContext.isOnlyNeedRawParseTree());
1095            }
1096        }
1097    }
1098
1099    @Override
1100    protected void performSemanticAnalysis(ParserContext context, TStatementList statements) {
1101        if (!TBaseType.isEnableResolver()) {
1102            return;
1103        }
1104        if (!this.getSyntaxErrors().isEmpty()) {
1105            return;
1106        }
1107        TSQLResolver resolver = new TSQLResolver(this.globalContext, statements);
1108        resolver.resolve();
1109    }
1110
1111    @Override
1112    protected void performInterpreter(ParserContext context, TStatementList statements) {
1113    }
1114
1115    @Override
1116    public String toString() {
1117        return "TeradataSqlParser{vendor=" + (Object)((Object)this.vendor) + "}";
1118    }
1119}