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 || this.gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstteradatacreatefunction) { 301 if (ast.tokencode == '(' || ast.tokencode == TBaseType.left_parenthesis_2) { 302 ++lcNestedParens; 303 } 304 if (ast.tokencode == ')' && --lcNestedParens < 0) { 305 lcNestedParens = 0; 306 } 307 if (ast.tokencode == TBaseType.rrw_begin) { 308 ++lcNestedBeginEnd; 309 } 310 if (ast.tokencode == TBaseType.rrw_end) { 311 boolean countThisEnd = true; 312 if (lcNestedSample > 0) { 313 // don't count sample...end 314 --lcNestedSample; 315 countThisEnd = false; 316 } else if (lcNestedCase > 0) { 317 // don't count case...end 318 --lcNestedCase; 319 countThisEnd = false; 320 } 321 if (countThisEnd) { 322 // don't count end while/if/case/loop/for/repeat 323 TSourceToken nt = ast.nextSolidToken(); 324 if (nt != null) { 325 countThisEnd = nt.tokencode != TBaseType.rrw_while 326 && nt.tokencode != TBaseType.rrw_if 327 && nt.tokencode != TBaseType.rrw_case 328 && nt.tokencode != TBaseType.rrw_loop 329 && nt.tokencode != TBaseType.rrw_for 330 && nt.tokencode != TBaseType.rrw_repeat; 331 } 332 } 333 if (countThisEnd && --lcNestedBeginEnd < 0) { 334 lcNestedBeginEnd = 0; 335 } 336 } 337 readyToEnd = lcNestedParens == 0 && lcNestedBeginEnd == 0; 338 } 339 // single stmt in function/procedure/trigger may use ; as terminate char 340 // so default terminate char is ;, if begin is found, then 341 // set terminate char to DelimiterChar 342 if (this.curdelimiterchar != this.delimiterChar) { 343 if (ast.tokencode == TBaseType.rrw_begin) { 344 this.curdelimiterchar = this.delimiterChar; 345 } else if (ast.tokencode == TBaseType.mslabel && ast.container.nextsolidtoken(ast, 1, false).tokencode == TBaseType.rrw_begin) { 346 this.curdelimiterchar = this.delimiterChar; 347 } 348 } 349 if (this.curdelimiterchar == ';') { 350 this.gcurrentsqlstatement.sourcetokenlist.add(ast); 351 if (ast.tokentype == ETokenType.ttsemicolon 352 && (this.gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstcreatetrigger 353 || this.gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstcreateprocedure 354 || this.gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstteradatacreatefunction) 355 && readyToEnd) { 356 gst = EFindSqlStateType.stnormal; 357 this.gcurrentsqlstatement.semicolonended = ast; 358 this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 359 continue block10; 360 } 361 } else { 362 if (ast.getAstext().length() == 1) { 363 ch = ast.getAstext().charAt(0); 364 } else if (ast.getAstext().length() > 1) { 365 ch = ast.getAstext().charAt(ast.getAstext().length() - 1); 366 } else { 367 ch = ' '; 368 } 369 if (ast.tokencode != TBaseType.cmtslashstar && ast.tokencode != TBaseType.cmtdoublehyphen 370 && ch == this.curdelimiterchar && ast.isFirstTokenOfLine() && readyToEnd) { 371 if (ast.getAstext().length() > 1) { 372 lcstr = ast.getAstext().substring(0, ast.getAstext().length() - 1); 373 int c2 = this.flexer.getkeywordvalue(lcstr); 374 if (c2 > 0) { 375 ast.tokencode = c2; 376 } 377 } else { 378 this.gcurrentsqlstatement.semicolonended = ast; 379 } 380 this.gcurrentsqlstatement.sourcetokenlist.add(ast); 381 gst = EFindSqlStateType.stnormal; 382 this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 383 } else if (ast.searchToken(TBaseType.rrw_create, 1) != null && ast.searchToken(TBaseType.rrw_procedure, 4) != null && readyToEnd) { 384 gst = EFindSqlStateType.stnormal; 385 this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 386 } else if (ast.tokencode == ';') { 387 this.gcurrentsqlstatement.sourcetokenlist.add(ast); 388 boolean startNewStatement = false; 389 if (readyToEnd) { 390 startNewStatement = this.sqlcmds.issql(ast.nextSolidToken(), gst, this.gcurrentsqlstatement) != null; 391 } 392 if (startNewStatement) { 393 gst = EFindSqlStateType.stnormal; 394 this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 395 } 396 } else { 397 this.gcurrentsqlstatement.sourcetokenlist.add(ast); 398 } 399 } 400 if (gst == EFindSqlStateType.ststoredprocedure && ast.tokencode == TBaseType.cmtdoublehyphen && ast.toString().trim().endsWith(TBaseType.sqlflow_stmt_delimiter_str)) { 401 gst = EFindSqlStateType.stnormal; 402 this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 403 } 404 } 405 } 406 } 407 if (TBaseType.assigned(this.gcurrentsqlstatement) && (gst == EFindSqlStateType.stsql || gst == EFindSqlStateType.stBTEQCmd || gst == EFindSqlStateType.ststoredprocedure || gst == EFindSqlStateType.sterror 408 || gst == EFindSqlStateType.stFastExportCmd || gst == EFindSqlStateType.stFastLoadCmd || gst == EFindSqlStateType.stMultiLoadCmd)) { 409 this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, true, builder); 410 } 411 builder.sqlStatements(this.sqlstatements); 412 builder.syntaxErrors(this.syntaxErrors instanceof ArrayList ? (ArrayList)this.syntaxErrors : new ArrayList(this.syntaxErrors)); 413 builder.errorCode(this.syntaxErrors.isEmpty() ? 0 : this.syntaxErrors.size()); 414 return errorcount; 415 } 416 417 private void handleLeftParenthesisToken(TSourceToken ast, int i) { 418 TSourceToken lcnextst = this.sourcetokenlist.nextsolidtoken(i, 1, false); 419 TSourceToken lcprevst = this.getprevsolidtoken(ast); 420 if ((lcprevst != null) && ( 421 (lcprevst.tokencode == TBaseType.rrw_teradata_cast) 422 || (lcprevst.tokencode == TBaseType.rrw_year) // sample sql: select year(date) - 1 mantisbt/view.php?id=3049 423 )) { 424 lcnextst = null; // don't check next token if prev token is cast. sample: cast(DATETIME as BIGINT) 425 } 426 if ((lcprevst != null) && (TFunctionCall.isBuiltIn(lcprevst.toString(), EDbVendor.dbvteradata))) { 427 if (((lcprevst.tokencode == TBaseType.rrw_end) 428 || (lcprevst.tokencode == TBaseType.rrw_time) 429 || (lcprevst.tokencode == TBaseType.rrw_date) 430 || (lcprevst.tokencode == TBaseType.rrw_timestamp) 431 || (lcprevst.tokencode == TBaseType.rrw_teradata_type) 432 || (lcprevst.toString().equalsIgnoreCase("current_date")) 433 || (lcprevst.toString().equalsIgnoreCase("current_time")) 434 || (lcprevst.toString().equalsIgnoreCase("current_timestamp")) 435 || (lcprevst.toString().equalsIgnoreCase("user")) 436 || (lcprevst.toString().equalsIgnoreCase("current_user")) 437 )) { 438 // Keep lcnextst as is 439 } else { 440 lcnextst = null; 441 } 442 } 443 if (lcnextst != null) { 444 EDataType dataType = TTypeName.searchTeradataTypeByName(lcnextst.toString()); 445 if ((dataType != null) && (lcprevst != null) && (lcprevst.tokencode != TBaseType.rrw_teradata_period)) { 446 ast.tokencode = TBaseType.rrw_teradata_start_data_conversion; 447 // lcnextst2 is the token 2 steps after ( 448 TSourceToken lcnextst2 = this.sourcetokenlist.nextsolidtoken(i, 2, false); 449 // lcnextst3 is the token 3 steps after ( 450 TSourceToken lcnextst3 = this.sourcetokenlist.nextsolidtoken(i, 3, false); 451 if (lcnextst2.tokencode == ',') { 452 // this is data conversion: (date,format '$xxx'), 453 // this is not data conversion: trunc(date,'yy') 454 TSourceToken st1 = lcnextst3; 455 TSourceToken st2 = this.sourcetokenlist.nextsolidtoken(i, 4, false); 456 if (TDatatypeAttribute.searchDataTypeAttributeByName(st1, st2) == null) { 457 // this is not data conversion, such as :trunc(date,'yy') 458 ast.tokencode = '('; 459 } 460 } else if (lcnextst2 != null) { 461 // lcnextst3 may be null when (date) is at end of query 462 this.handleDataTypeConversion(ast, dataType, lcnextst2, lcnextst3, lcprevst); 463 } 464 } else { 465 TSourceToken st1 = lcnextst; 466 TSourceToken st2 = this.sourcetokenlist.nextsolidtoken(i, 2, false); 467 if (TDatatypeAttribute.searchDataTypeAttributeByName(st1, st2) != null) { 468 ast.tokencode = TBaseType.rrw_teradata_start_data_conversion; 469 } 470 } 471 } 472 } 473 474 private void handleDataTypeConversion(TSourceToken ast, EDataType dataType, TSourceToken lcnextst2, TSourceToken lcnextst3, TSourceToken lcprevst) { 475 switch (dataType) { 476 case date_t: 477 case time_t: 478 // next token is: ) or comma or data attribute token 479 if ((lcnextst2.tokencode == ')') 480 || ((lcnextst2.tokencode == '(') && (lcnextst3 != null) && (lcnextst3.tokencode == TBaseType.iconst))) { // hire_date (time(6)) 481 // ident(DATE) / ident(TIME): keep the data-conversion reading. 482 // Teradata documents column_name(data_type) as CAST shorthand, and 483 // whitespace before '(' is not significant, so col1(DATE) and 484 // col1 (DATE) must produce the same parse tree (Mantis 4647 - the 485 // earlier whitespace-based disambiguation from Mantis 4023 silently 486 // corrupted JOIN predicates like o.order_ts(DATE)). 487 // The genuinely ambiguous case - a KNOWN function taking the bare 488 // DATE/TIME literal, e.g. TD_WEDNESDAY(date) - never reaches this 489 // branch: built-in function names (including the TD_* day functions 490 // in teradata_function.properties) are filtered by the 491 // TFunctionCall.isBuiltIn check above, which leaves their '(' 492 // untouched so they parse as function calls. 493 } else { 494 ast.tokencode = '('; 495 } 496 break; 497 case timestamp_t: 498 case interval_t: 499 if (lcnextst2.tokencode == TBaseType.sconst) { // INSERT t1 (TIME '10:44:25.123-08:00',TIMESTAMP '2000-09-20 10:44:25.1234') 500 ast.tokencode = '('; 501 } 502 break; 503 case period_t: 504 if (lcnextst2.tokencode == '(') { 505 // 只有 period() 才确保这里 period 是datatype,否则period不是datatype 506 } else { 507 ast.tokencode = '('; 508 } 509 break; 510 case char_t: 511 if (lcnextst2.tokencode == ')') { 512 // NULL (CHAR) AS B 513 } 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') 514 ast.tokencode = '('; 515 } 516 break; 517 default: 518 // A cast-shorthand like (NUMBER(10,2)) always carries an integer 519 // constant as its first precision argument. When the token after the 520 // type-name's '(' is anything else — e.g. a macro/procedure parameter 521 // named like a type: CREATE MACRO m (number (INTEGER)) — this is not 522 // a data conversion, so restore the plain parenthesis. 523 if ((lcnextst2 != null) && (lcnextst2.tokencode == '(') 524 && ((lcnextst3 == null) || (lcnextst3.tokencode != TBaseType.iconst))) { 525 ast.tokencode = '('; 526 } 527 break; 528 } 529 } 530 531 private void handleForToken(TSourceToken ast) { 532 TSourceToken nextToken = ast.searchTokenAfterObjectName(); 533 if (nextToken != null) { 534 if (nextToken.tokencode == TBaseType.rrw_as) { 535 ast.tokencode = TBaseType.rrw_teradata_for_loop; 536 } 537 } 538 } 539 540 private void handleLastToken(TSourceToken ast) { 541 TSourceToken nextToken = ast.searchToken('(', 1); 542 if (nextToken != null) { 543 ast.tokencode = TBaseType.rrw_last_function; 544 } 545 } 546 547 private void handlePivotToken(TSourceToken ast) { 548 TSourceToken nextToken = ast.searchToken('(', 1); 549 if (nextToken == null) { 550 ast.tokencode = TBaseType.ident; 551 } 552 } 553 554 private void handlePeriodToken(TSourceToken ast) { 555 TSourceToken nextToken = ast.nextSolidToken(); 556 if (nextToken == null) { 557 ast.tokencode = TBaseType.ident; 558 } else if (nextToken.tokencode == '(') { 559 // keep it as keyword 560 } else if (nextToken.tokencode == TBaseType.rrw_for) { 561 // keep it as keyword 562 } else if (nextToken.tokencode == TBaseType.sconst) { 563 // sample: DEFAULT PERIOD '(2005-02-03, 2006-02-03)' 564 // keep it as keyword 565 } else { 566 if (ast.prevSolidToken() == null) { 567 ast.tokencode = TBaseType.ident; 568 } else { 569 if (!ast.prevSolidToken().toString().equalsIgnoreCase("anchor")) { 570 ast.tokencode = TBaseType.ident; 571 } 572 } 573 } 574 } 575 576 private void handleTransactionToken(TSourceToken ast) { 577 TSourceToken lcprevst = this.getprevsolidtoken(ast); 578 if (lcprevst != null) { 579 if (lcprevst.tokencode == TBaseType.rrw_end) { 580 lcprevst.tokencode = TBaseType.rrw_teradata_end_t; 581 } 582 } 583 } 584 585 private void handleReplaceToken(TSourceToken ast) { 586 TSourceToken nextToken = ast.searchToken('(', 1); 587 if (nextToken != null) { 588 ast.tokencode = TBaseType.ident; 589 } 590 } 591 592 private void handleBetweenToken(TSourceToken ast) { 593 TSourceToken nextToken = ast.nextSolidToken(); 594 if (nextToken != null) { 595 if ((nextToken.tokencode == TBaseType.rrw_begin) || (nextToken.tokencode == TBaseType.rrw_end)) { 596 // BETWEEN begin(DODD_EFF_PERI) 597 nextToken.tokencode = TBaseType.ident; 598 } 599 } 600 } 601 602 private void handleDeclareToken(TSourceToken ast) { 603 TSourceToken nextToken = ast.nextSolidToken(); 604 if (nextToken != null) { 605 TSourceToken nextnextToken = nextToken.nextSolidToken(); 606 if (nextnextToken != null) { 607 if ((nextnextToken.toString().equalsIgnoreCase("cursor")) 608 || (nextnextToken.toString().equalsIgnoreCase("insensitive")) 609 || (nextnextToken.toString().equalsIgnoreCase("scroll")) 610 || (nextnextToken.toString().equalsIgnoreCase("no"))) { 611 nextToken.tokencode = TBaseType.rrw_teradata_cursor_name; 612 } else if ((nextnextToken.toString().equalsIgnoreCase("condition"))) { 613 nextToken.tokencode = TBaseType.rrw_teradata_condition_name; 614 } 615 } 616 } 617 } 618 619 private void handleCaseToken(TSourceToken ast) { 620 int lcNest = 1; 621 for (int m = ast.posinlist + 1; m < this.sourcetokenlist.size() - 1; m++) { 622 TSourceToken st = this.sourcetokenlist.get(m); 623 if (st.tokencode == TBaseType.rrw_case) { 624 st.tag = 1; // this is nested case keyword, don't check this token later 625 lcNest++; 626 } else if (st.tokencode == TBaseType.rrw_begin) { 627 // BEGIN...END blocks consume an END token, track them 628 lcNest++; 629 } 630 if (st.tokencode == TBaseType.rrw_end) { 631 TSourceToken nextSolid = st.nextSolidToken(); 632 // END IF, END WHILE, END LOOP, END FOR, END REPEAT close their own blocks, 633 // not our CASE — skip them entirely 634 if (nextSolid != null && (nextSolid.tokencode == TBaseType.rrw_if 635 || nextSolid.tokencode == TBaseType.rrw_while 636 || nextSolid.tokencode == TBaseType.rrw_loop 637 || nextSolid.tokencode == TBaseType.rrw_for 638 || nextSolid.tokencode == TBaseType.rrw_repeat)) { 639 continue; 640 } 641 lcNest--; 642 if (lcNest == 0) { 643 if (nextSolid != null) { 644 if (nextSolid.tokencode == TBaseType.rrw_case) { 645 ast.tokencode = TBaseType.rrw_teradata_case_stmt; 646 nextSolid.tag = 1; // this is case after end, don't check this token later 647 } 648 } 649 // Always break after finding the matching END, whether it's followed by CASE or not 650 // This prevents CASE expressions from incorrectly matching with later "END CASE" constructs 651 break; 652 } 653 } 654 } 655 } 656 657 private void handleGrantToken(TSourceToken ast) { 658 TSourceToken prevSolidToken = ast.prevSolidToken(); 659 if (prevSolidToken != null) { 660 if (prevSolidToken.tokencode == TBaseType.rrw_with) { 661 prevSolidToken.tokencode = TBaseType.rrw_teradata_with_grant; 662 } 663 } 664 } 665 666 private void handleVariableToken(TSourceToken ast) { 667 // USING ( _spVV8 VARCHAR(1024) CHARACTER SET UNICODE ) SELECT INTO :_spVV8 ; 668 // variable after into keyword treat as identifier 669 TSourceToken prevSolidToken = ast.prevSolidToken(); 670 if (prevSolidToken != null) { 671 if (prevSolidToken.tokencode == TBaseType.rrw_into) { 672 ast.tokencode = TBaseType.ident; 673 } 674 } 675 } 676 677 private void handleSequencedToken(TSourceToken ast) { 678 TSourceToken nextSolidToken = ast.nextSolidToken(); 679 if (nextSolidToken != null) { 680 if ((nextSolidToken.tokencode == TBaseType.rrw_teradata_validtime)) { 681 TSourceToken nextToken2 = nextSolidToken.nextSolidToken(); 682 if (nextToken2 != null) { 683 if ((nextToken2.tokencode == TBaseType.rrw_select) 684 || (nextToken2.tokencode == TBaseType.rrw_insert) 685 || (nextToken2.tokencode == TBaseType.rrw_update) 686 || (nextToken2.tokencode == TBaseType.rrw_delete) 687 || (nextToken2.tokencode == TBaseType.rrw_merge)) { 688 // SEQUENCED VALIDTIME select/insert/update/delete/merge 689 ast.tokencode = TBaseType.lexspace; 690 nextSolidToken.tokencode = TBaseType.lexspace; 691 } 692 } 693 } 694 } 695 } 696 697 private EFindSqlStateType handleMultiLoadCmdState(TSourceToken ast, EFindSqlStateType gst, SqlParseResult.Builder builder) { 698 this.gcurrentsqlstatement.sourcetokenlist.add(ast); 699 if ((ast.tokencode == ';') && (ast.isLastTokenOfLine())) { 700 TSourceToken nextToken = ast.nextSolidToken(); 701 TSourceToken nextToken2 = nextToken != null ? nextToken.nextSolidToken() : null; 702 if (nextToken != null && nextToken2 != null && 703 nextToken.tokencode == '.' && 704 nextToken2.toString().equalsIgnoreCase("FIELD") && 705 nextToken.isFirstTokenOfLine()) { 706 // Remain in stMultiLoadCmd state 707 } else { 708 this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 709 gst = EFindSqlStateType.stnormal; 710 } 711 } else if ((ast.tokencode == ';') && this.isSqlStyleSetMultiLoadCmd()) { 712 // A SQL-style `SET` directive (e.g. SET QUERY_BAND = '...' FOR SESSION) 713 // is terminated by its own ';' even when it is NOT the last token of 714 // the line. Without this, a statement that follows the ';' on the same 715 // line (SET QUERY_BAND = 'A=1'; SELECT c1 FROM t1;) is swallowed into 716 // the SET directive and never reaches downstream processing (MantisBT 717 // 4584). The ';' inside a band-specification literal is part of an 718 // SCONST token, not a ';' token, so it never triggers this branch. 719 this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 720 gst = EFindSqlStateType.stnormal; 721 } else if (ast.isLastTokenOfLine()) { 722 // .IF ERRORCODE <> 0 THEN 723 // INSERT INTO Sou_EMP_Tab 724 // (1, 'bala'); 725 String cmdstr = this.gcurrentsqlstatement.sourcetokenlist.get(0).toString(); 726 if (this.gcurrentsqlstatement.sourcetokenlist.size() > 1) { 727 cmdstr = cmdstr + this.gcurrentsqlstatement.sourcetokenlist.get(1).toString(); 728 } 729 if ((cmdstr.toLowerCase().startsWith(".if")) || (cmdstr.toLowerCase().startsWith(".set")) || (cmdstr.toLowerCase().startsWith("set"))) { 730 // A SQL-style SET directive (SET QUERY_BAND = '...') may continue on the 731 // next line with FOR SESSION / FOR TRANSACTION and is terminated by its 732 // own ';'. Do not cut it at end-of-line when the next solid token is FOR. 733 TSourceToken continuation = ast.nextSolidToken(); 734 boolean sqlSetContinues = this.isSqlStyleSetMultiLoadCmd() 735 && continuation != null && continuation.tokencode == TBaseType.rrw_for; 736 if (!sqlSetContinues) { 737 this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 738 gst = EFindSqlStateType.stnormal; 739 } 740 } 741 } 742 return gst; 743 } 744 745 /** 746 * True when the MultiLoad-state command currently being collected is a 747 * SQL-style {@code SET} directive — one whose first solid token is the bare 748 * {@code SET} keyword (for example {@code SET QUERY_BAND = '...' FOR SESSION}). 749 * <p> 750 * These directives are session-level SQL statements that terminate at ';', 751 * unlike genuine MultiLoad utility directives which are dot-prefixed 752 * ({@code .SET}, {@code .FIELD}, ...) and are line-based. Restricting the 753 * mid-line ';' termination to bare {@code SET} keeps real dot-commands 754 * untouched. 755 */ 756 private boolean isSqlStyleSetMultiLoadCmd() { 757 if (this.gcurrentsqlstatement == null) return false; 758 for (int i = 0; i < this.gcurrentsqlstatement.sourcetokenlist.size(); i++) { 759 TSourceToken t = this.gcurrentsqlstatement.sourcetokenlist.get(i); 760 if (!t.issolidtoken()) continue; 761 return t.tokencode == TBaseType.rrw_set; 762 } 763 return false; 764 } 765 766 private EFindSqlStateType handleFastExportCmdState(TSourceToken ast, EFindSqlStateType gst, SqlParseResult.Builder builder) { 767 this.gcurrentsqlstatement.sourcetokenlist.add(ast); 768 if ((ast.tokencode == ';') && (ast.isLastTokenOfLine())) { 769 this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 770 gst = EFindSqlStateType.stnormal; 771 } else { 772 boolean findSQL = false; 773 TSourceToken nextst = ast.nextToken(); 774 if (nextst != null) { 775 if (nextst.tokentype == ETokenType.ttreturn) { 776 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 } 782 } 783 } 784 return gst; 785 } 786 787 private EFindSqlStateType handleFastLoadCmdState(TSourceToken ast, EFindSqlStateType gst, SqlParseResult.Builder builder) { 788 this.gcurrentsqlstatement.sourcetokenlist.add(ast); 789 if ((ast.tokencode == ';') && (ast.isLastTokenOfLine())) { 790 this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 791 gst = EFindSqlStateType.stnormal; 792 } else if (ast.tokencode == TBaseType.lexnewline) { 793 TSourceToken nextst = ast.nextSolidToken(); 794 if (nextst != null) { 795 if (nextst.tokencode == TBaseType.BTEQCMD) { 796 this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 797 gst = EFindSqlStateType.stFastLoadCmd; 798 } else if (nextst.tokencode == '.') { 799 this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 800 gst = EFindSqlStateType.stFastLoadCmd; 801 } else if (this.sqlcmds.issql(nextst, gst, this.gcurrentsqlstatement) != null) { 802 this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 803 gst = EFindSqlStateType.stnormal; 804 } 805 } 806 } 807 return gst; 808 } 809 810 private EFindSqlStateType handleBTEQCmdState(TSourceToken ast, SqlParseResult.Builder builder) { 811 EFindSqlStateType gst = EFindSqlStateType.stBTEQCmd; 812 this.gcurrentsqlstatement.sourcetokenlist.add(ast); 813 814 if (ast.tokencode == TBaseType.lexnewline) { 815 if (this.isContinueBTEQCmd) { 816 this.isContinueBTEQCmd = false; 817 } else if (this.isInIfThenStatement) { 818 // Continue collecting tokens for .IF/.ELSEIF THEN until semicolon 819 // Don't complete the statement on newline 820 } else { 821 if (((TTeradataBTEQCmd)this.gcurrentsqlstatement).getBteqCmdType() == BteqCmdType.LOGON) { 822 boolean findSQL = (this.sqlcmds.issql(ast.nextSolidToken(), gst, this.gcurrentsqlstatement) != null); 823 if (findSQL) { 824 this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 825 gst = EFindSqlStateType.stnormal; 826 } 827 } else { 828 this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 829 gst = EFindSqlStateType.stnormal; 830 } 831 } 832 } else if ((ast.tokencode == '-') && (ast.isLastTokenOfLine())) { 833 if (!this.inBTEQComment) { 834 this.isContinueBTEQCmd = true; 835 } 836 } else if (ast.tokencode == TBaseType.lexspace) { 837 // Do nothing 838 } else if (ast.tokencode == ';') { 839 // Reset IF THEN state when statement ends 840 this.isInIfThenStatement = false; 841 this.onRawStatementComplete(this.parserContext, this.gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 842 boolean findNewCmdInSameLine = this.checkForNewBTEQCmdInSameLine(ast); 843 if (findNewCmdInSameLine) { 844 gst = EFindSqlStateType.stBTEQCmd; 845 } else { 846 gst = EFindSqlStateType.stnormal; 847 } 848 } else if (ast.tokencode == '*') { 849 this.isContinueBTEQCmd = false; 850 } else { // solid token 851 // Check if this is a THEN keyword at end of line for .IF/.ELSEIF commands 852 // In this case, the BTEQ command should continue to include the following SQL statement 853 BteqCmdType cmdType = ((TTeradataBTEQCmd)this.gcurrentsqlstatement).getBteqCmdType(); 854 if ((cmdType == BteqCmdType.IF || cmdType == BteqCmdType.ELSEIF) 855 && ast.tokencode == TBaseType.rrw_then 856 && ast.isLastTokenOfLine()) { 857 this.isContinueBTEQCmd = true; 858 this.isInIfThenStatement = true; 859 } else { 860 this.isContinueBTEQCmd = false; 861 } 862 } 863 return gst; 864 } 865 866 private boolean checkForNewBTEQCmdInSameLine(TSourceToken ast) { 867 boolean findNewCmdInSameLine = false; 868 if (!ast.isLastTokenOfLine()) { 869 // check if next cmd like below: 870 // .SET SIDETITLES ON; .SET FOLDLINE ON ALL 871 TSourceToken nextst = ast.nextSolidToken(); 872 if ((nextst != null) && (nextst.tokencode == '.')) { 873 TSourceToken nextst2 = nextst.nextSolidToken(); 874 TSourceToken searchToken = null; 875 if (nextst2 != null) { 876 if (nextst2.tokencode == TBaseType.rrw_set) { 877 TSourceToken nextst3 = nextst2.nextSolidToken(); 878 if (nextst3 != null) { 879 searchToken = nextst3; 880 } 881 } else { 882 searchToken = nextst2; 883 } 884 885 BteqCmdType bteqCmdType = BteqCmdType.searchBteqCmd(searchToken); 886 if (bteqCmdType != null) { 887 this.gcurrentsqlstatement = new TTeradataBTEQCmd(this.vendor); 888 ((TTeradataBTEQCmd)this.gcurrentsqlstatement).setBteqCmdType(bteqCmdType); 889 this.gcurrentsqlstatement.sourcetokenlist.add(ast); 890 findNewCmdInSameLine = true; 891 } 892 } 893 } 894 } 895 return findNewCmdInSameLine; 896 } 897 898 private EFindSqlStateType handleNormalState(TSourceToken ast, EFindSqlStateType gst, TSourceToken lcprevsolidtoken, SqlParseResult.Builder builder) { 899 if ((ast.tokencode == TBaseType.cmtdoublehyphen) 900 || (ast.tokencode == TBaseType.cmtslashstar) 901 || (ast.tokencode == TBaseType.lexspace) 902 || (ast.tokencode == TBaseType.lexnewline) 903 || (ast.tokentype == ETokenType.ttsemicolon)) { 904 if (TBaseType.assigned(this.gcurrentsqlstatement)) { 905 this.gcurrentsqlstatement.sourcetokenlist.add(ast); 906 } 907 908 if ((lcprevsolidtoken != null) && (ast.tokentype == ETokenType.ttsemicolon)) { 909 if (lcprevsolidtoken.tokentype == ETokenType.ttsemicolon) { 910 // ;;;; continuous semicolon, treat it as comment 911 ast.tokentype = ETokenType.ttsimplecomment; 912 ast.tokencode = TBaseType.cmtdoublehyphen; 913 } 914 } 915 916 return gst; 917 } 918 if ((gst = this.checkForBTEQOrUtilityCommand(ast, gst)) != EFindSqlStateType.stnormal) { 919 return gst; 920 } 921 922 // find a tokentext to start sql or stored procedure mode 923 this.gcurrentsqlstatement = this.sqlcmds.issql(ast, gst, this.gcurrentsqlstatement); 924 925 if (TBaseType.assigned(this.gcurrentsqlstatement)) { 926 ESqlStatementType[] ses = {ESqlStatementType.sstcreateprocedure, ESqlStatementType.sstteradatacreatefunction, ESqlStatementType.sstcreatetrigger}; 927 if (this.includesqlstatementtype(this.gcurrentsqlstatement.sqlstatementtype, ses)) { 928 gst = EFindSqlStateType.ststoredprocedure; 929 this.gcurrentsqlstatement.sourcetokenlist.add(ast); 930 if ((this.gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstcreateprocedure) 931 || (this.gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstteradatacreatefunction) 932 || (this.gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstcreatetrigger)) { 933 this.curdelimiterchar = ';'; 934 } 935 } else { 936 gst = EFindSqlStateType.stsql; 937 this.gcurrentsqlstatement.sourcetokenlist.add(ast); 938 } 939 } 940 941 if ((!TBaseType.assigned(this.gcurrentsqlstatement)) && (ast.isFirstTokenOfLine())) { 942 gst = this.flexer.cmdType(ast); 943 if ((gst == EFindSqlStateType.stMultiLoadCmd) || (gst == EFindSqlStateType.stFastLoadCmd) || (gst == EFindSqlStateType.stFastExportCmd)) { 944 // SQL-style `SET <variable> = <expression>` is a procedural assignment that 945 // ends with ';' and may span multiple lines. MultiLoad/BTEQ SET directives do 946 // not use '=' — if '=' follows the identifier we are looking at a SQL SET. 947 if (ast.tokencode == TBaseType.rrw_set && isSqlStyleSetAssignment(ast)) { 948 this.gcurrentsqlstatement = new TSetStmt(this.vendor); 949 this.gcurrentsqlstatement.sqlstatementtype = ESqlStatementType.sstset; 950 this.gcurrentsqlstatement.sourcetokenlist.add(ast); 951 gst = EFindSqlStateType.stsql; 952 return gst; 953 } else { 954 this.gcurrentsqlstatement = new TTeradataBTEQCmd(this.vendor); 955 ((TTeradataBTEQCmd)this.gcurrentsqlstatement).setFindSqlStateType(gst); 956 this.gcurrentsqlstatement.sourcetokenlist.add(ast); 957 return gst; 958 } 959 } else { 960 gst = EFindSqlStateType.stnormal; 961 } 962 } 963 964 if (!TBaseType.assigned(this.gcurrentsqlstatement)) { // error tokentext found 965 this.syntaxErrors.add(new TSyntaxError(ast.getAstext(), ast.lineNo, (ast.columnNo < 0 ? 0 : ast.columnNo), 966 "Error when tokenlize", EErrorType.spwarning, TBaseType.MSG_WARNING_ERROR_WHEN_TOKENIZE, null, ast.posinlist)); 967 968 ast.tokentype = ETokenType.tttokenlizererrortoken; 969 gst = EFindSqlStateType.sterror; 970 971 this.gcurrentsqlstatement = new TUnknownSqlStatement(this.vendor); 972 this.gcurrentsqlstatement.sqlstatementtype = ESqlStatementType.sstinvalid; 973 this.gcurrentsqlstatement.sourcetokenlist.add(ast); 974 } 975 return gst; 976 } 977 978 /** 979 * SQL-style procedural SET variable assignment where the right-hand side 980 * is a parenthesized expression/subquery: {@code SET <name> = (<expr>)}. 981 * This is distinct from Teradata BTEQ/MultiLoad SET directives (which use 982 * space-separated values, no '=', terminate at end-of-line) and from 983 * SET statements whose tail is vendor-specific BTEQ syntax (for example 984 * {@code SET QUERY_BAND = '...' UPDATE FOR SESSION}). 985 * <p> 986 * Restricting the detection to RHS starting with '(' keeps the fix tightly 987 * scoped to the reported failure (MantisBT 3336) — a subquery-driven 988 * assignment that legitimately spans multiple lines and terminates at ';'. 989 */ 990 private boolean isSqlStyleSetAssignment(TSourceToken setToken) { 991 TSourceToken next = setToken.nextSolidToken(); 992 if (next == null) return false; 993 TSourceToken afterNext = next.nextSolidToken(); 994 if (afterNext == null || afterNext.tokencode != '=') return false; 995 TSourceToken rhs = afterNext.nextSolidToken(); 996 return rhs != null && rhs.tokencode == '('; 997 } 998 999 private EFindSqlStateType checkForBTEQOrUtilityCommand(TSourceToken ast, EFindSqlStateType gst) { 1000 // Check for .bteq commands: either "." at start of line, or "." after ";" at start of line 1001 boolean isDotAtStartOfLine = (ast.tokencode == '.') && (ast.isFirstTokenOfLine()); 1002 boolean isDotAfterSemicolonAtStartOfLine = false; 1003 if ((ast.tokencode == '.') && !isDotAtStartOfLine) { 1004 TSourceToken prevToken = ast.prevSolidToken(); 1005 if ((prevToken != null) && (prevToken.tokencode == ';') && (prevToken.isFirstTokenOfLine())) { 1006 isDotAfterSemicolonAtStartOfLine = true; 1007 } 1008 } 1009 1010 if (isDotAtStartOfLine || isDotAfterSemicolonAtStartOfLine) { 1011 // .bteq commands 1012 TSourceToken nextst = ast.nextSolidToken(); 1013 if (nextst != null) { 1014 gst = this.flexer.cmdType(nextst); 1015 boolean foundCmd = false; 1016 switch (gst) { 1017 case stMultiLoadCmd: 1018 this.gcurrentsqlstatement = new TTeradataMultiLoadCmd(this.vendor); 1019 gst = EFindSqlStateType.stMultiLoadCmd; 1020 ((TTeradataMultiLoadCmd)this.gcurrentsqlstatement).setCmdType(TTeradataHelper.searchMultiLoadTypeByName(nextst.getAstext())); 1021 foundCmd = true; 1022 break; 1023 case stFastExportCmd: 1024 this.gcurrentsqlstatement = new TTeradataFastExportCmd(this.vendor); 1025 gst = EFindSqlStateType.stFastExportCmd; 1026 foundCmd = true; 1027 break; 1028 case stFastLoadCmd: 1029 this.gcurrentsqlstatement = new TTeradataFastLoadCmd(this.vendor); 1030 gst = EFindSqlStateType.stFastLoadCmd; 1031 foundCmd = true; 1032 break; 1033 case stBTEQCmd: 1034 this.gcurrentsqlstatement = new TTeradataBTEQCmd(this.vendor); 1035 ((TTeradataBTEQCmd)this.gcurrentsqlstatement).setBteqCmdType(BteqCmdType.searchBteqCmd(nextst)); 1036 gst = EFindSqlStateType.stBTEQCmd; 1037 foundCmd = true; 1038 break; 1039 default: 1040 break; 1041 } 1042 if (foundCmd) { 1043 this.gcurrentsqlstatement.sourcetokenlist.add(ast); 1044 return gst; 1045 } 1046 } 1047 } else if ((ast.tokencode == '*') && (ast.isFirstTokenOfLine())) { 1048 // BTEQ comment: * comment 1049 gst = EFindSqlStateType.stBTEQCmd; 1050 this.inBTEQComment = true; // Mark that we're in a BTEQ comment 1051 this.gcurrentsqlstatement = new TTeradataBTEQCmd(this.vendor); 1052 ((TTeradataBTEQCmd)this.gcurrentsqlstatement).setBteqCmdType(BteqCmdType.COMMENT); 1053 this.gcurrentsqlstatement.sourcetokenlist.add(ast); 1054 return gst; 1055 } else if ((ast.tokencode == '=') && (ast.isFirstTokenOfLine())) { 1056 // BTEQ command starting with = 1057 TSourceToken st1 = ast.prevSolidToken(); 1058 if ((st1 != null) && (st1.tokencode == ';')) { 1059 gst = EFindSqlStateType.stBTEQCmd; 1060 this.gcurrentsqlstatement = new TTeradataBTEQCmd(this.vendor); 1061 ((TTeradataBTEQCmd)this.gcurrentsqlstatement).setFindSqlStateType(gst); 1062 this.gcurrentsqlstatement.sourcetokenlist.add(ast); 1063 return gst; 1064 } 1065 } else if ((ast.isFirstTokenOfLine()) && (BteqCmdType.searchBteqCmd(ast) != null)) { 1066 TSourceToken st1 = ast.prevSolidToken(); 1067 if ((st1 != null) && (st1.tokencode == ';')) { 1068 gst = EFindSqlStateType.stBTEQCmd; 1069 this.gcurrentsqlstatement = new TTeradataBTEQCmd(this.vendor); 1070 ((TTeradataBTEQCmd)this.gcurrentsqlstatement).setFindSqlStateType(gst); 1071 this.gcurrentsqlstatement.sourcetokenlist.add(ast); 1072 return gst; 1073 } 1074 } else if ((ast.tokencode == '&') && (ast.isFirstTokenOfLine())) { 1075 // Teradata macro/substitution variable: &VARIABLE_NAME at start of line 1076 // Treat it as a BTEQ-like command that ends at end of line 1077 TSourceToken nextst = ast.nextSolidToken(); 1078 if (nextst != null && (nextst.tokencode == TBaseType.ident || nextst.tokentype == ETokenType.ttidentifier)) { 1079 gst = EFindSqlStateType.stBTEQCmd; 1080 this.gcurrentsqlstatement = new TTeradataBTEQCmd(this.vendor); 1081 ((TTeradataBTEQCmd)this.gcurrentsqlstatement).setFindSqlStateType(gst); 1082 this.gcurrentsqlstatement.sourcetokenlist.add(ast); 1083 return gst; 1084 } 1085 } 1086 return EFindSqlStateType.stnormal; 1087 } 1088 1089 1090 private boolean includesqlstatementtype(ESqlStatementType type, ESqlStatementType[] types) { 1091 for (ESqlStatementType t : types) { 1092 if (type != t) continue; 1093 return true; 1094 } 1095 return false; 1096 } 1097 1098 @Override 1099 protected TStatementList performParsing(ParserContext context, TCustomParser parser, TCustomParser secondaryParser, TSourceTokenList tokens, TStatementList rawStatements) { 1100 this.fparser = (TParserTeradata)parser; 1101 this.sourcetokenlist = tokens; 1102 this.parserContext = context; 1103 this.sqlstatements = rawStatements; 1104 if (this.sqlstatements == null) { 1105 this.sqlstatements = new TStatementList(); 1106 return this.sqlstatements; 1107 } 1108 if (this.sqlcmds == null) { 1109 this.sqlcmds = SqlCmdsFactory.get(this.vendor); 1110 } 1111 this.fparser.sqlcmds = this.sqlcmds; 1112 1113 // Initialize global context for semantic analysis 1114 // CRITICAL: When delegated from TGSqlParser, use TGSqlParser's frameStack 1115 // so that variables set in SET statements can be found by EXECUTE statements 1116 if (context != null && context.getGsqlparser() != null) { 1117 gudusoft.gsqlparser.TGSqlParser gsqlparser = (gudusoft.gsqlparser.TGSqlParser) context.getGsqlparser(); 1118 this.frameStack = gsqlparser.getFrameStack(); 1119 1120 // CRITICAL: Set gsqlparser on the NodeFactory - matches TGSqlParser behavior 1121 // This is needed for proper AST node creation during parsing 1122 this.fparser.getNf().setGsqlParser(gsqlparser); 1123 1124 // Create global context if needed 1125 this.globalContext = new gudusoft.gsqlparser.compiler.TContext(); 1126 this.sqlEnv = new gudusoft.gsqlparser.sqlenv.TSQLEnv(this.vendor) { 1127 @Override 1128 public void initSQLEnv() { 1129 } 1130 }; 1131 this.globalContext.setSqlEnv(this.sqlEnv, this.sqlstatements); 1132 } else { 1133 this.initializeGlobalContext(); 1134 } 1135 1136 for (int i = 0; i < this.sqlstatements.size(); ++i) { 1137 TCustomSqlStatement stmt = this.sqlstatements.getRawSql(i); 1138 try { 1139 stmt.setFrameStack(this.frameStack); 1140 int parseResult = stmt.parsestatement(null, false, context.isOnlyNeedRawParseTree()); 1141 boolean doRecover = TBaseType.ENABLE_ERROR_RECOVER_IN_CREATE_TABLE; 1142 if (doRecover && (parseResult != 0 || stmt.getErrorCount() > 0)) { 1143 this.handleCreateTableErrorRecovery(stmt); 1144 } 1145 if (parseResult == 0 && stmt.getErrorCount() <= 0) continue; 1146 this.copyErrorsFromStatement(stmt); 1147 continue; 1148 } 1149 catch (Exception ex) { 1150 ex.printStackTrace(); 1151 this.handleStatementParsingException(stmt, i, ex); 1152 } 1153 } 1154 if (this.globalFrame != null) { 1155 this.globalFrame.popMeFromStack(this.frameStack); 1156 } 1157 return this.sqlstatements; 1158 } 1159 1160 private void handleCreateTableErrorRecovery(TCustomSqlStatement stmt) { 1161 if (!(stmt.sqlstatementtype != ESqlStatementType.sstcreatetable && stmt.sqlstatementtype != ESqlStatementType.sstcreateindex || TBaseType.c_createTableStrictParsing)) { 1162 int nested = 0; 1163 boolean isIgnore = false; 1164 boolean isFoundIgnoreToken = false; 1165 TSourceToken firstIgnoreToken = null; 1166 for (int k = 0; k < stmt.sourcetokenlist.size(); ++k) { 1167 TSourceToken st = stmt.sourcetokenlist.get(k); 1168 if (isIgnore) { 1169 if (st.issolidtoken() && st.tokencode != 59) { 1170 isFoundIgnoreToken = true; 1171 if (firstIgnoreToken == null) { 1172 firstIgnoreToken = st; 1173 } 1174 } 1175 if (st.tokencode == 59) continue; 1176 st.tokencode = 273; 1177 continue; 1178 } 1179 if (st.tokencode == 41 && --nested == 0) { 1180 isIgnore = true; 1181 } 1182 if (st.tokencode != 40) continue; 1183 ++nested; 1184 } 1185 if (isFoundIgnoreToken) { 1186 stmt.parsestatement(null, false, this.parserContext.isOnlyNeedRawParseTree()); 1187 } 1188 } 1189 } 1190 1191 @Override 1192 protected void performSemanticAnalysis(ParserContext context, TStatementList statements) { 1193 if (!TBaseType.isEnableResolver()) { 1194 return; 1195 } 1196 if (!this.getSyntaxErrors().isEmpty()) { 1197 return; 1198 } 1199 TSQLResolver resolver = new TSQLResolver(this.globalContext, statements); 1200 resolver.resolve(); 1201 } 1202 1203 @Override 1204 protected void performInterpreter(ParserContext context, TStatementList statements) { 1205 } 1206 1207 @Override 1208 public String toString() { 1209 return "TeradataSqlParser{vendor=" + (Object)((Object)this.vendor) + "}"; 1210 } 1211}