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