001package gudusoft.gsqlparser; 002 003 004import gudusoft.gsqlparser.compiler.*; 005import gudusoft.gsqlparser.nodes.*; 006import gudusoft.gsqlparser.nodes.dax.TDaxFunction; 007import gudusoft.gsqlparser.nodes.teradata.THashByClause; 008import gudusoft.gsqlparser.sqlenv.TSQLEnv; 009import gudusoft.gsqlparser.sqlenv.TSQLFunction; 010import gudusoft.gsqlparser.stmt.*; 011import gudusoft.gsqlparser.stmt.dax.TDaxStmt; 012import gudusoft.gsqlparser.stmt.oracle.*; 013import gudusoft.gsqlparser.sqlenv.ESQLDataObjectType; 014import gudusoft.gsqlparser.util.OraclePseudoColumnUtil; 015import gudusoft.gsqlparser.util.SQLUtil; 016import gudusoft.gsqlparser.util.functionChecker; 017import gudusoft.gsqlparser.util.keywordChecker; 018 019import java.util.ArrayDeque; 020import java.util.ArrayList; 021import java.util.Deque; 022import java.util.Stack; 023import java.util.TreeMap; 024import java.security.MessageDigest; 025import java.security.NoSuchAlgorithmException; 026import java.nio.charset.StandardCharsets; 027 028/** 029 * TCustomSqlStatement is the root class for all SQL statements. 030 */ 031public class TCustomSqlStatement extends TParseTreeNode implements IRelation{ 032 033 private String sqlHash; 034 035 public void setSqlHash(String sqlHash) { 036 this.sqlHash = sqlHash; 037 } 038 039 /** 040 * Returns a stable, vendor-aware hash of this statement's SQL text for 041 * lineage grouping and statement identity. 042 * <p> 043 * Purpose: 044 * <ul> 045 * <li>Provide a deterministic identifier for a statement that is 046 * insensitive to formatting (whitespace, comments, keyword case).</li> 047 * <li>Act as the first component of a recommended {@code statementKey} 048 * for lineage grouping: {@code statementKey = sqlHash + "#" + queryId}.</li> 049 * </ul> 050 * How it works: 051 * <ul> 052 * <li>Builds a normalized textual representation of the statement by 053 * iterating the token chain between {@code getStartToken()} and 054 * {@code getEndToken()}.</li> 055 * <li>Normalization rules (Profile A by default): remove comments; 056 * collapse spacing deterministically; uppercase keywords; handle 057 * identifiers by the current vendor's case-sensitivity 058 * ({@link gudusoft.gsqlparser.sqlenv.TSQLEnv#columnCollationCaseSensitive}). 059 * For delimited identifiers (quoted identifiers), the quotes are 060 * removed via {@link gudusoft.gsqlparser.TBaseType#removeQuoteChar(String)} 061 * before case normalization. String literals are kept as-is.</li> 062 * <li>The hash input is prefixed by a normalization version and the 063 * current vendor, so future evolution of the normalizer will not break 064 * previously computed values: {@code normVersion + "\n" + vendor + "\n" + normalizedSql}.</li> 065 * <li>Hash function: SHA-256 (lowercase hex).</li> 066 * </ul> 067 * Usage: 068 * <ul> 069 * <li>Call {@code getSqlHash(false)} for cached result or lazy 070 * calculation with Profile A (identity-safe) normalization.</li> 071 * <li>Call {@code getSqlHash(true)} to force recalculation (e.g., after 072 * mutating the underlying token chain).</li> 073 * <li>If you need a grouping-friendly variant (masking certain literal 074 * classes), use {@link #computeSqlHash(SqlNormalizationProfile, String)} 075 * with {@link SqlNormalizationProfile#GROUPING_FRIENDLY}.</li> 076 * </ul> 077 * 078 * @param forceReCalculate if true, recompute the hash even if a cached 079 * value exists 080 * @return lowercase hex SHA-256 hash of the normalized SQL text 081 */ 082 public String getSqlHash(boolean forceReCalculate) { 083 if (sqlHash != null && !forceReCalculate) { 084 return sqlHash; 085 } 086 // Default: Profile A (identity-safe) 087 this.sqlHash = computeSqlHash(SqlNormalizationProfile.IDENTITY_SAFE, DEFAULT_SQLHASH_NORM_VERSION); 088 return this.sqlHash; 089 } 090 091 /** 092 * Backward compatible overload equivalent to {@code getSqlHash(false)}. 093 */ 094 public String getSqlHash() { 095 return getSqlHash(false); 096 } 097 098 /** 099 * Normalization profiles for SQL hashing. 100 * <ul> 101 * <li>IDENTITY_SAFE: Remove comments and normalize spacing/case only. 102 * Literal values are preserved. Operator synonyms are minimally 103 * unified (e.g., {@code !=} to {@code <>}).</li> 104 * <li>GROUPING_FRIENDLY: In addition to IDENTITY_SAFE, date/time 105 * string literals are normalized to {@code '1970-01-01'} to aid 106 * grouping across different runs where only timestamps vary.</li> 107 * </ul> 108 */ 109 public enum SqlNormalizationProfile { 110 IDENTITY_SAFE, 111 GROUPING_FRIENDLY 112 } 113 114 private static final String DEFAULT_SQLHASH_NORM_VERSION = "sqlHash.norm.v1"; 115 116 /** 117 * Compute a SQL hash using the given normalization profile and version. 118 * See {@link #getSqlHash(boolean)} for details. 119 * 120 * @param profile normalization profile 121 * @param normVersion version tag embedded in the hash input 122 * @return lowercase hex SHA-256 hash 123 */ 124 public String computeSqlHash(SqlNormalizationProfile profile, String normVersion) { 125 String normalized = toNormalizedSql(profile); 126 String input = normVersion + "\n" + String.valueOf(this.dbvendor) + "\n" + normalized; 127 return sha256Hex(input); 128 } 129 130 /** 131 * Produce a normalized textual representation of this statement according 132 * to the supplied profile. The method is non-mutating: it does not alter 133 * token texts or statuses permanently. 134 * 135 * Rules applied: 136 * - Remove comments. 137 * - Remove trailing semicolon. 138 * - Deterministic spacing around punctuation/operators. 139 * - Uppercase keywords, keep string literals as-is. 140 * - Identifiers: if the vendor's column collation is case sensitive, keep 141 * identifier case; otherwise uppercase. For quoted identifiers, remove 142 * quoting via {@link TBaseType#removeQuoteChar(String)} prior to case 143 * handling. 144 * - Minimal operator unification: {@code "!="} becomes {@code "<>"}. 145 * - Profile B adds date/time string masking: {@code '1970-01-01'}. 146 * 147 * @param profile normalization profile 148 * @return normalized SQL string 149 */ 150 public String toNormalizedSql(SqlNormalizationProfile profile) { 151 TSourceToken start = getStartToken(); 152 TSourceToken end = getEndToken(); 153 if (start == null || end == null) { 154 return String.valueOf(this.toString()); 155 } 156 157 boolean idCaseSensitive = Boolean.TRUE.equals(TSQLEnv.columnCollationCaseSensitive.get(this.dbvendor)); 158 159 StringBuilder sb = new StringBuilder(256); 160 TokenClass prevClass = null; 161 char lastAppended = '\0'; 162 163 // We may need to skip a single trailing semicolon that is the end token 164 // Detect once to simplify checks in the loop 165 boolean endIsSemicolon = (end.toString().equals(";")); 166 167 TSourceToken cur = start; 168 while (cur != null) { 169 // Skip tokens that should not contribute 170 if (isComment(cur)) { 171 // skip comments 172 } else if (cur == end && endIsSemicolon) { 173 // drop trailing semicolon 174 } else if (cur.tokenstatus == ETokenStatus.tsdeleted || cur.tokenstatus == ETokenStatus.tsignorebyyacc) { 175 // ignore deleted/ignored tokens 176 } else { 177 String text = normalizeTokenText(cur, idCaseSensitive, profile); 178 if (text != null && !text.isEmpty()) { 179 TokenClass clazz = classify(cur, text); 180 if (shouldAddSpaceBefore(prevClass, clazz, lastAppended)) { 181 sb.append(' '); 182 lastAppended = ' '; 183 } 184 sb.append(text); 185 lastAppended = text.charAt(text.length() - 1); 186 prevClass = clazz; 187 } 188 } 189 190 if (cur == end) { 191 break; 192 } else { 193 cur = cur.getNextTokenInChain(); 194 } 195 } 196 197 // Trim any trailing single space for cleanliness 198 int len = sb.length(); 199 if (len > 0 && sb.charAt(len - 1) == ' ') { 200 sb.setLength(len - 1); 201 } 202 return sb.toString(); 203 } 204 205 private static boolean isComment(TSourceToken t) { 206 return t.tokentype == ETokenType.ttsimplecomment || t.tokentype == ETokenType.ttbracketedcomment; 207 } 208 209 private enum TokenClass { WORD, OP, DOT, COMMA, PAREN_LEFT, PAREN_RIGHT, SEMICOLON, OTHER } 210 211 private static TokenClass classify(TSourceToken t, String normalizedText) { 212 switch (t.tokentype) { 213 case ttidentifier: 214 case ttdqstring: 215 case ttdbstring: 216 case ttbrstring: 217 case ttnumber: 218 case ttsqstring: 219 case ttkeyword: 220 case ttnonreservedkeyword: 221 case ttbindvar: 222 case ttsqlvar: 223 case ttsubstitutionvar: 224 return TokenClass.WORD; 225 case ttperiod: 226 return TokenClass.DOT; 227 case ttcomma: 228 return TokenClass.COMMA; 229 case ttleftparenthesis: 230 return TokenClass.PAREN_LEFT; 231 case ttrightparenthesis: 232 return TokenClass.PAREN_RIGHT; 233 case ttsemicolon: 234 case ttsemicolon2: 235 case ttsemicolon3: 236 return TokenClass.SEMICOLON; 237 case ttequals: 238 case ttplussign: 239 case ttminussign: 240 case ttasterisk: 241 case ttslash: 242 case ttgreaterthan: 243 case ttlessthan: 244 case ttsinglecharoperator: 245 case ttmulticharoperator: 246 case ttconcatenationop: 247 return TokenClass.OP; 248 default: 249 break; 250 } 251 // Heuristic: treat UNKNOWN single-char punctuation as operator 252 if (normalizedText.length() == 1 && !Character.isLetterOrDigit(normalizedText.charAt(0))) { 253 return TokenClass.OP; 254 } 255 return TokenClass.OTHER; 256 } 257 258 private static boolean shouldAddSpaceBefore(TokenClass prev, TokenClass curr, char lastAppended) { 259 if (prev == null) return false; 260 if (lastAppended == '\0') return false; 261 262 // No space rules 263 if (curr == TokenClass.DOT || curr == TokenClass.COMMA || curr == TokenClass.PAREN_RIGHT) return false; 264 if (prev == TokenClass.DOT || prev == TokenClass.PAREN_LEFT) return false; 265 266 // Space around operators and between words 267 if (prev == TokenClass.OP && (curr == TokenClass.WORD || curr == TokenClass.PAREN_LEFT)) return true; 268 if ((prev == TokenClass.WORD || prev == TokenClass.PAREN_RIGHT) && (curr == TokenClass.OP || curr == TokenClass.WORD)) return true; 269 270 // After comma ensure a space before next word 271 if (prev == TokenClass.COMMA && (curr == TokenClass.WORD || curr == TokenClass.PAREN_LEFT)) return true; 272 273 // Default: no space 274 return false; 275 } 276 277 private String normalizeTokenText(TSourceToken t, boolean idCaseSensitive, SqlNormalizationProfile profile) { 278 String s = t.toString(); 279 280 // Minimal operator unification: != => <> 281 if (t.tokentype == ETokenType.ttmulticharoperator || t.tokentype == ETokenType.ttsinglecharoperator || 282 t.tokentype == ETokenType.ttgreaterthan || t.tokentype == ETokenType.ttlessthan) { 283 if ("!=".equals(s)) { 284 return "<>"; 285 } 286 return s; 287 } 288 289 switch (t.tokentype) { 290 case ttsimplecomment: 291 case ttbracketedcomment: 292 return null; // removed 293 case ttkeyword: 294 case ttnonreservedkeyword: 295 return s.toUpperCase(); 296 case ttidentifier: { 297 // regular identifier 298 return idCaseSensitive ? s : s.toUpperCase(); 299 } 300 case ttdqstring: 301 case ttdbstring: 302 case ttbrstring: { 303 // delimited/quoted identifier -> remove quotes, then apply case rule 304 String unquoted = TBaseType.removeQuoteChar(s); 305 return idCaseSensitive ? unquoted : unquoted.toUpperCase(); 306 } 307 case ttsqstring: { 308 if (profile == SqlNormalizationProfile.GROUPING_FRIENDLY && looksLikeDateOrTimestampLiteral(s)) { 309 return "'1970-01-01'"; 310 } 311 return s; // keep string literal as-is 312 } 313 default: 314 return s; 315 } 316 } 317 318 private static boolean looksLikeDateOrTimestampLiteral(String s) { 319 // Very lightweight check for common SQL string date/timestamp formats, e.g. '2025-09-14' or '2025-09-14 12:34:56' 320 // Input includes surrounding quotes per token text. 321 if (s == null || s.length() < 2) return false; 322 if (!(s.charAt(0) == '\'' && s.charAt(s.length() - 1) == '\'')) return false; 323 String inner = s.substring(1, s.length() - 1).trim(); 324 // yyyy-mm-dd or yyyy-mm-dd hh:mm[:ss[.fff]] 325 if (inner.matches("\\d{4}-\\d{2}-\\d{2}")) return true; 326 if (inner.matches("\\d{4}-\\d{2}-\\d{2}[ T]\\d{2}:\\d{2}(:\\d{2}(\\.\\d{1,9})?)?")) return true; 327 return false; 328 } 329 330 private static String sha256Hex(String input) { 331 try { 332 MessageDigest md = MessageDigest.getInstance("SHA-256"); 333 byte[] out = md.digest(input.getBytes(StandardCharsets.UTF_8)); 334 char[] hex = new char[out.length * 2]; 335 final char[] digits = "0123456789abcdef".toCharArray(); 336 for (int i = 0, j = 0; i < out.length; i++) { 337 int b = out[i] & 0xFF; 338 hex[j++] = digits[(b >>> 4) & 0x0F]; 339 hex[j++] = digits[b & 0x0F]; 340 } 341 return new String(hex); 342 } catch (NoSuchAlgorithmException e) { 343 // Should never happen on a standard JVM 344 throw new RuntimeException("SHA-256 not available", e); 345 } 346 } 347 348 private String queryId; 349 350 public void setQueryId(String queryId) { 351 this.queryId = queryId; 352 } 353 354 /** 355 * Retrieves the unique and stable identifier for this SQL statement. 356 * <p> 357 * The queryId provides a reliable way to reference any statement, including subqueries, 358 * within a parsed SQL script. It is generated hierarchically based on the statement's 359 * position within the Abstract Syntax Tree (AST), ensuring that the ID is reproducible 360 * across identical SQL inputs. 361 * <p> 362 * <b>ID Format:</b> 363 * <ul> 364 * <li>A top-level statement has an ID like {@code "stmt_0_select"}, where {@code 0} is the index 365 * of the statement in the script and {@code select} is the statement type.</li> 366 * <li>A nested statement will have a path-like ID that includes its parent's ID. For example, 367 * an {@code INSERT} statement containing a {@code SELECT} subquery might have an ID for the 368 * subquery like {@code "stmt_0_insert#stmt_1_select"}.</li> 369 * </ul> 370 * This identifier is particularly useful for tasks like data lineage analysis, where tracking 371 * the origin and transformation of data through various statements is required. 372 * 373 * @return The unique query identifier string for this statement, or {@code null} if it has not been set. 374 */ 375 public String getQueryId() { 376 return queryId; 377 } 378 379 public void setUsingVariableList(TColumnDefinitionList usingVariableList) { 380 this.usingVariableList = usingVariableList; 381 } 382 383 private TColumnDefinitionList usingVariableList; 384 385 /* 386 * Variables defined in teradata using clause. 387 * */ 388 public TColumnDefinitionList getUsingVariableList() { 389 return usingVariableList; 390 } 391 392 protected ArrayList<TAttributeNode> relationAttributes = new ArrayList<>(); 393 394 @Override 395 public ArrayList<TAttributeNode> getAttributes(){ 396 // if (relationAttributes.size() != 0) return relationAttributes; 397 relationAttributes.clear(); 398 for(TTable table:relations){ 399 //relationAttributes.addAll(table.getAttributes()); 400 TAttributeNode.addAllNodesToList(table.getAttributes(),relationAttributes); 401 } 402 403 return relationAttributes; 404 } 405 406 @Override 407 public String getRelationName(){ 408 return null; 409 } 410 411 @Override 412 public int size(){ 413 return relationAttributes.size(); 414 } 415 416 @Override 417 public String toScript(){ 418 String ret = super.toScript(); 419 if ((ret == null)||(ret.isEmpty())){ 420 ret = this.toString(); 421 }else{ 422 if ((this.getEndToken() != null) && (this.getEndToken().tokencode == ';')) { 423 if (!ret.endsWith(";")){ 424 ret = ret + ";"; 425 } 426 } 427 } 428 return ret; 429 } 430 431 protected TFromClause fromClause; 432 433 public void setFromClause(TFromClause fromClause) { 434 this.fromClause = fromClause; 435 } 436 437 public TFromClause getFromClause() { 438 return fromClause; 439 } 440 441 /** 442 * Relations that used in from clause of select statement. 443 * Or tables of other statements such as insert, update, delete and etc 444 * 445 * Please use this property to get the relations instead of {@link #getTables()} and {@link #getJoins()}after version 2.7.4.0 446 * 447 * when a join is used in from clause, then the table in getRelations() is type of ETableSource.join, and you can 448 * use TTable.getJoinExpr() to get this join. 449 * 450 * @return 451 */ 452 public ArrayList<TTable> getRelations() { 453 return relations; 454 } 455 456 private ArrayList<TTable> relations = new ArrayList<>(); 457 458 protected TTable fromSourceTable; 459 460 /** 461 * This is table in from clause if only one table is listed in the from clause, 462 * If more than one table is listed in from clause, please check {@link #getFromSourceJoin()} instead. 463 * 464 * @return table in from clause 465 */ 466 public TTable getFromSourceTable() { 467 return fromSourceTable; 468 } 469 470 /** 471 * This is a join in from clause, including left and right relation. 472 * If only a single table is listed in from clause, please use {@link #getFromSourceTable()} instead 473 * @return 474 */ 475 public TJoinExpr getFromSourceJoin() { 476 return fromSourceJoin; 477 } 478 479 protected TJoinExpr fromSourceJoin; 480 481 private String asCanonicalText = null; 482 483 /** 484 * this method return a canonical form of a SQL statement in plan text. 485 * <br>1. remove all comment inside SQL query. 486 * <br>2. remove redundant parenthesis at the begin/end of a select statement. 487 * <br>3. replace all number in where clause with 999 constant 488 * <br>4. replace all string constant in where clause with 'placeholder_str' 489 * <br>5. all number elements in a list such as (1,2,3,4) will be change to a single element (999) 490 * <br>6. all string elements in a list such as ('a','b','c','d') will be change to a single element ('placeholder_str') 491 * 492 * @return a canonical form of a SQL statement in plan text. 493 */ 494 public String asCanonical(){ 495 if (asCanonicalText != null) return asCanonicalText; 496 497 String ret = null; 498 TSourceToken lcStartToken = getStartToken(); 499 if (lcStartToken == null) return toString(); 500 TSourceToken lcEndToken = getEndToken(); 501 if (lcEndToken == null) return toString(); 502 503 // remove the ; token at the end of statement 504 if (lcEndToken.tokencode == ';') lcEndToken.tokenstatus = ETokenStatus.tsdeleted; 505 506 507 // remove ( ) at the begin and end of the statement 508 TSourceToken lcCurrentToken = lcStartToken; 509 while (lcCurrentToken != null){ 510 if (lcCurrentToken.tokencode != '(') { 511 break; 512 }else{ 513 if (lcCurrentToken.getLinkToken() != null){ 514 lcCurrentToken.tokenstatus = ETokenStatus.tsdeleted; 515 lcCurrentToken.getLinkToken().tokenstatus = ETokenStatus.tsdeleted; 516 } 517 } 518 519 if (lcCurrentToken.equals(lcEndToken)){ 520 break; 521 }else{ 522 lcCurrentToken = lcCurrentToken.getNextTokenInChain(); 523 } 524 } 525 526 // change constant to placeholder, all number change to 999 and string constant change to placeholder_str 527 constantVisitor cv = new constantVisitor(); 528 this.acceptChildren(cv); 529 530 531 boolean chainUnchanged = true, includingComment = false; 532 StringBuffer sb = new StringBuffer(""); 533 TSourceToken lcPrevSt = null; 534 boolean ignoreNextReturnToken = false, isChainModified = false; 535 536 lcCurrentToken = lcStartToken; 537 while (lcCurrentToken != null){ 538 if((lcCurrentToken.tokenstatus == ETokenStatus.tsdeleted) 539 ||(!includingComment && ((lcCurrentToken.tokencode == TBaseType.cmtslashstar) ||(lcCurrentToken.tokencode == TBaseType.cmtdoublehyphen))) 540 ){ 541 // ignore this token, do nothing 542 //System.out.println("out: ignore deleted token:"+lcCurrentToken.astext); 543 }else{ 544 // 545 sb.append(lcCurrentToken.toString()); 546 if (lcCurrentToken.isChangedInAsCanonical()){ 547 lcCurrentToken.restoreText(); 548 } 549 } 550 551 if (lcCurrentToken.equals(lcEndToken)){ 552 break; 553 }else{ 554 lcCurrentToken = lcCurrentToken.getNextTokenInChain(); 555 } 556 557 } 558 asCanonicalText = sb.toString(); 559 return asCanonicalText; 560 } 561 562 private TCTE cteIncludeThisStmt = null; 563 564 public void setCteIncludeThisStmt(TCTE cteIncludeThisStmt) { 565 this.cteIncludeThisStmt = cteIncludeThisStmt; 566 } 567 568 public TCTE getCteIncludeThisStmt() { 569 return cteIncludeThisStmt; 570 } 571 572 private TreeMap<String,TResultColumn> expandedResultColumns = null; 573 574 public TreeMap<String,TResultColumn> getExpandedResultColumns() { 575 if (expandedResultColumns == null){ 576 expandedResultColumns = new TreeMap<>(); 577 } 578 return expandedResultColumns; 579 } 580 581 public TSQLFunction searchFunctionInSQLEnv(String functionName){ 582 if (getSqlEnv() == null) return null; 583 return getSqlEnv().searchFunction(functionName); 584 } 585 586 public TSQLEnv getSqlEnv() { 587 if (getGlobalScope() == null) return null; 588 return getGlobalScope().getSqlEnv(); 589 } 590 591 public TGlobalScope getGlobalScope() { 592 TGlobalScope lcResult = null; 593 if (frameStack != null){ 594 if (frameStack.get(0) != null){ 595 lcResult = (TGlobalScope)frameStack.get(0).getScope(); 596 } 597 } 598 return lcResult; 599 } 600 601 private Stack<TFrame> frameStack; 602 603 public void setFrameStack(Stack<TFrame> frameStack) { 604 this.frameStack = frameStack; 605 } 606 607 public Stack<TFrame> getFrameStack() { 608 return frameStack; 609 } 610 611 private TPTNodeList<TColumnWithSortOrder> indexColumns = null; 612 613 public TPTNodeList<TColumnWithSortOrder> getIndexColumns() { 614 return indexColumns; 615 } 616 617 private Stack<TObjectName> variableStack = null; 618 619 public void setVariableStack(Stack<TObjectName> variableStack) { 620 this.variableStack = variableStack; 621 } 622 623 public Stack<TObjectName> getVariableStack() { 624 if (variableStack == null){ 625 variableStack = new Stack<TObjectName>(); 626 } 627 628 return variableStack; 629 } 630 631 private Stack<TDaxFunction> daxFunctionStack = null; 632 633 public Stack<TDaxFunction> getDaxFunctionStack() { 634 if (daxFunctionStack == null){ 635 daxFunctionStack = new Stack<TDaxFunction>(); 636 } 637 return daxFunctionStack; 638 } 639 640 private TObjectName labelName; 641 642 public void setLabelName(TObjectName lName) { 643 labelName = lName; 644 if (labelName != null){ 645 //labelName.setObjectType(TObjectName.ttobjLabelName); 646 labelName.setDbObjectType(EDbObjectType.label); 647 } 648 } 649 650 /** 651 * 652 * @return label name used in plsql statement. 653 */ 654 public TObjectName getLabelName() { 655 656 return labelName; 657 } 658 659 660 private TObjectName endlabelName; 661 662 public void setEndlabelName(TObjectName endlabelName) { 663 this.endlabelName = endlabelName; 664 } 665 666 public TObjectName getEndlabelName() { 667 668 return endlabelName; 669 } 670 671 /** 672 * Type of this statement. 673 */ 674 public ESqlStatementType sqlstatementtype; 675 /** 676 * Source tokens included in this statement. only source tokens available when this is a top level statement, otherwise, there is no source token in this statement. 677 * Please check {@link gudusoft.gsqlparser.nodes.TParseTreeNode#getStartToken()}, and {@link gudusoft.gsqlparser.nodes.TParseTreeNode#getEndToken()} of this statement. 678 */ 679 public TSourceTokenList sourcetokenlist; 680 681 public TSourceTokenList getTokenList() { 682 return sourcetokenlist; 683 } 684 /** 685 * Parser used to parse this statement. 686 */ 687 public TCustomParser parser; 688 /** 689 * PLSQL parser used to parse this statement. 690 */ 691 public TCustomParser plsqlparser; 692 /** 693 * Tag used by parser internally. 694 */ 695 public int dummytag; 696 697 /** 698 * target table in the delete/insert/update/create table statement. 699 * @see #joins 700 * @see TSelectSqlStatement 701 * @see TDeleteSqlStatement 702 * @see TUpdateSqlStatement 703 * @see TCreateTableSqlStatement 704 * @see gudusoft.gsqlparser.stmt.TMergeSqlStatement 705 */ 706 public TTable getTargetTable() { 707 return targetTable; 708 } 709 710 public void setTargetTable(TTable targetTable) { 711 setNewSubNode(this.targetTable,targetTable,getAnchorNode()); 712 this.targetTable = targetTable; 713 } 714 715 private TTable targetTable ; 716 717 /** 718 * joins represents table sources in the from clause. All structure information was reserved. 719 * <p>SQL 1: 720 * <p><blockquote><pre>select f from t1</pre></blockquote> 721 * <p>size of joins will be 1, t1 can be fetch via joins.getJoin(0).getTable() 722 * <p> 723 * <p>SQL 2: 724 * <p><blockquote><pre>select f from t1,t2</pre></blockquote> 725 * <p>size of joins will be 2, 726 * <p>t1 can be fetch via joins.getJoin(0).getTable() 727 * <p>t2 can be fetch via joins.getJoin(1).getTable() 728 * <p> 729 * <p>SQL 3: 730 * <p><blockquote><pre>select f from t1 join t2 on t1.f1 = t2.f1</pre></blockquote> 731 * <p>size of joins will be 1, 732 * <p>t1 information can be fetch via joins.getJoin(0).getTable() 733 * <p>In order to access t2, we need to introduce a new class {@link TJoinItem} which includes all information about t2 and join condition. 734 * <p>There is a property named joinItems of {@link TJoin} which is type of {@link TJoinItemList} that includes a list of {@link TJoinItem}. 735 * <p>this property can be access via {@link gudusoft.gsqlparser.nodes.TJoin#getJoinItems()}. 736 * <p>Now, t2 can be fetch via joins.getJoin(0).getJoinItems().getJoinItem(0).getTable() 737 * <p> 738 * <p>SQL 4: 739 * <p><blockquote><pre>select f from t1 join t2 on t1.f1 = t2.f1 join t3 on t1.f1 = t3.f1</pre></blockquote> 740 * <p>size of joins will be 1, 741 * <p>t1 can be fetch via joins.getJoin(0).getTable() 742 * <p>t2 can be fetch via joins.getJoin(0).getJoinItems().getJoinItem(0).getTable() 743 * <p>t3 can be fetch via joins.getJoin(0).getJoinItems().getJoinItem(1).getTable() 744 * 745 * @see #tables 746 */ 747 public TJoinList joins; 748 749 /** 750 * Provides a quick way to access all tables involved in this SQL statement. 751 * <p>It stores all tables in a flat way while {@link #joins} stores all tables in a hierarchical structure. 752 * <p>joins only represents tables in from clause of select/delete statement, and tables in update/insert statement. 753 * <p>{@link #tables} includes all tables in all types of SQL statements such as tables involved in a create table or create trigger statements. 754 */ 755 public TTableList tables; 756 757 public TJoinList getJoins() { 758 return joins; 759 } 760 761 public TTableList getTables() { 762 return tables; 763 } 764 765 /** 766 * Saves all first level sub statements. 767 * <p>By iterating statements recursively, you can fetch all included statements in an easy way. 768 * <p><blockquote><pre> 769 * select f1+(select f2 from t2) from t1 770 * where f2 > all (select f3 from t3 where f4 = (select f5 from t4))</pre> 771 * </blockquote> 772 * <p> Statements included in above SQL was save in a hierarchical way like this: 773 * <ul> 774 * <li>(select f2 from t2)</li> 775 * <li>(select f3 from t3 where f4 = (select f5 from t4)) 776 * <ul> 777 * <li>(select f5 from t4)</li> 778 * </ul> 779 * </li> 780 * </ul> 781 * <p>If this statement is a create procedure/function statement, then all declaration statements and statements in 782 * procedure body can also be fetched quickly by iterating this property recursively. 783 * 784 * 785 */ 786 public TStatementList getStatements() { 787 if (statements == null){ 788 statements = new TStatementList(); 789 } 790 return statements; 791 } 792 793 private TStatementList statements; 794 795 public void setCteList(TCTEList cteList) { 796 setNewSubNode(this.cteList,cteList,getAnchorNode()); 797 this.cteList = cteList; 798 } 799 800 /** 801 * Multiple common table expressions {@link TCTE} can be specified following the single WITH keyword. 802 *<p> Each common table expression specified can also be referenced by name in the FROM clause of subsequent common table expressions. 803 * 804 * <p>Used in select, delete, update statement. 805 * @return List of common table expression. 806 */ 807 808 public TCTEList getCteList() { 809 810 return cteList; 811 } 812 813 private TCTEList cteList = null; 814 815 public void setResultColumnList(TResultColumnList resultColumnList) { 816 setNewSubNode(this.resultColumnList,resultColumnList,getAnchorNode()); 817 this.resultColumnList = resultColumnList; 818 } 819 820 /** 821 * In select statement, this method returns Items in select_list. 822 * Can be *, expr, and name.* 823 * <br><br> 824 * In update statement, this method returns assignments in set clause. 825 * 826 * @return select list of select statement or assignments of update statement. 827 */ 828 public TResultColumnList getResultColumnList() { 829 830 return resultColumnList; 831 } 832 833 private TResultColumnList resultColumnList = null; 834 835 private TWhereClause whereClause = null; 836 private TTopClause topClause = null; 837 private TOutputClause outputClause = null; 838 private TReturningClause returningClause = null; 839 840 public void setReturningClause(TReturningClause returningClause) { 841 setNewSubNode(this.returningClause,returningClause,getAnchorNode()); 842 this.returningClause = returningClause; 843 } 844 845 /** 846 * @return {@link TReturningClause returning clause.} 847 */ 848 849 public TReturningClause getReturningClause() { 850 851 return returningClause; 852 } 853 854 public void setOutputClause(TOutputClause outputClause) { 855 setNewSubNode(this.outputClause,outputClause,getAnchorNode()); 856 this.outputClause = outputClause; 857 } 858 859 /** 860 * @return output clause. 861 */ 862 863 public TOutputClause getOutputClause() { 864 865 return outputClause; 866 } 867 868 public void setTopClause(TTopClause topClause) { 869 setNewSubNode(this.topClause,topClause,getAnchorNode()); 870 this.topClause = topClause; 871 } 872 873 /** 874 * @return {@link TTopClause top clause.} 875 */ 876 public TTopClause getTopClause() { 877 return topClause; 878 } 879 880 public void setWhereClause(TWhereClause newWhereClause){ 881 setNewSubNode(this.whereClause ,newWhereClause,getAnchorNode()); 882 this.whereClause = newWhereClause; 883 } 884 885 886 /** 887 * @deprecated As of 2.0.9.0, use {@link #setWhereClause(TWhereClause)} instead 888 * Or, use {@link TWhereClause#setText(String)} 889 * 890 * @param condition 891 * @return 892 */ 893 public TWhereClause addWhereClause(String condition){ 894 return this.whereClause; 895 } 896 897 /** 898 * restrict the rows selected to those that satisfy one or more conditions. 899 * used in select, delete, update statement. 900 * @return {@link TWhereClause where clause.} 901 */ 902 public TWhereClause getWhereClause() { 903 return whereClause; 904 } 905 906 public void setAlreadyAddToParent(boolean alreadyAddToParent) { 907 this.alreadyAddToParent = alreadyAddToParent; 908 } 909 910 private boolean alreadyAddToParent = false; 911 912 private boolean ableToIncludeCTE(ESqlStatementType sst){ 913 return ((sst == ESqlStatementType.sstselect) 914 ||(sst == ESqlStatementType.sstupdate) 915 ||(sst == ESqlStatementType.sstinsert) 916 ||(sst == ESqlStatementType.sstdelete) 917 ); 918 } 919 920 private TCTEList cteListInAllLevels = new TCTEList(); 921 922 protected TCTEList searchCTEList(Boolean stopAtFirstFinding){ 923 cteListInAllLevels.clear(); 924 if (cteList != null ) { 925 cteListInAllLevels.addAll(cteList); 926 if (stopAtFirstFinding) return cteListInAllLevels; 927 } 928 929 TCustomSqlStatement lcParent = this.parentStmt; 930 while (lcParent != null){ 931 if (!ableToIncludeCTE(lcParent.sqlstatementtype)) break; 932 933 if (lcParent.cteList != null) { 934 cteListInAllLevels.addAll(lcParent.cteList); 935 if (stopAtFirstFinding) break; 936 } 937 lcParent = lcParent.parentStmt; 938 } 939 return cteListInAllLevels; 940 } 941 942 943// protected TCTEList searchCTEList(){ 944// TCTEList ret = null; 945// if (cteList != null ) {return cteList;} 946// TCustomSqlStatement lcParent = this.parentStmt; 947// while (lcParent != null){ 948// if (!ableToIncludeCTE(lcParent.sqlstatementtype)) break; 949// ret = lcParent.cteList; 950// if (ret != null) break; 951// lcParent = lcParent.parentStmt; 952// } 953// return ret; 954// } 955 956 public TCustomSqlStatement getParentStmt() { 957 return parentStmt; 958 } 959 960 public TParseTreeNode getParentObjectName(){ 961 TParseTreeNode result = super.getParentObjectName(); 962 if (result != null) return result; 963 return getParentStmt(); 964 } 965 966 public void setParentStmt(TCustomSqlStatement parentStmt) { 967 if (!alreadyAddToParent){ 968 this.parentStmt = parentStmt; 969 if (this.parentStmt != null){ 970 parentStmt.getStatements().add(this); 971 alreadyAddToParent = true; 972 } 973 } 974 } 975 976 public void setParentStmtToNull() { 977 this.parentStmt = null; 978 } 979 980 private TCustomSqlStatement ancestorStmt = null; 981 982 public TCustomSqlStatement getAncestorStmt() { 983 TCustomSqlStatement lcRet = this; 984 while (lcRet.getParentStmt() != null){ 985 lcRet = lcRet.getParentStmt(); 986 } 987 return lcRet; 988 } 989 990 /** 991 * parent statement of this statement if any 992 */ 993 private TCustomSqlStatement parentStmt = null; 994 995 /** 996 * Original Parse tree node from parser 997 */ 998 public TParseTreeNode rootNode; 999 1000 private Stack symbolTable = null; 1001 1002 /** 1003 * 1004 * @deprecated since ver 2.5.3.5, please use {@link TStmtScope} instead 1005 */ 1006 public Stack getSymbolTable() { 1007 if (symbolTable == null){ 1008 symbolTable = new Stack(); 1009 } 1010 return symbolTable; 1011 } 1012 1013 public TSourceToken semicolonended; 1014 public boolean isctequery; 1015 private ArrayList <TSyntaxError> syntaxErrors; 1016 1017 public ArrayList<TSyntaxError> getSyntaxErrors() { 1018 return syntaxErrors; 1019 } 1020 1021 public String getErrormessage(){ 1022 1023 String s="",hint="Syntax error"; 1024 TSyntaxError t; 1025 for (int i= 0; i< syntaxErrors.size(); i++) 1026 { 1027 t = (TSyntaxError) syntaxErrors.get(i); 1028 if (t.hint.length() > 0) hint = t.hint; 1029 s= s+hint+"("+t.errorno+") near: "+t.tokentext; 1030 s=s+"("+t.lineNo; 1031 s=s+","+t.columnNo +")"; 1032 //s=s+" expected tokentext:"+t.hint; 1033 1034 // break;//get only one message, remove this one and uncomment next line to get all error messages 1035 if (i != syntaxErrors.size() - 1) 1036 s = s +TBaseType.linebreak; 1037 } 1038 1039 return s; 1040 } 1041 1042 public ArrayList<TSyntaxError> getSyntaxHints() { 1043 return syntaxHints; 1044 } 1045 1046 private ArrayList <TSyntaxError> syntaxHints; 1047 1048 protected boolean isparsed; 1049 TSourceToken _semicolon; 1050 1051 /** 1052 * Number of syntax errors for this statement. 1053 * @return 0 means no syntax error. 1054 */ 1055 public int getErrorCount() { 1056 return syntaxErrors.size(); 1057 } 1058 1059 1060 public TCustomSqlStatement(EDbVendor dbvendor){ 1061 super(); 1062 this.dbvendor = dbvendor; 1063 sqlstatementtype = ESqlStatementType.sstunknown; 1064 dummytag = 0; 1065 sourcetokenlist = new TSourceTokenList(); 1066 syntaxErrors = new ArrayList<TSyntaxError>(4); 1067 syntaxHints = new ArrayList<TSyntaxError>(4); 1068 tables = new TTableList(); 1069 joins = new TJoinList(); 1070 indexColumns = new TPTNodeList<TColumnWithSortOrder>(); 1071 } 1072 1073 /** 1074 * Log error messages if syntax errors found while parsing this statement. 1075 * @param se syntax error structure. 1076 * @return type of error 1077 */ 1078 public EActionOnParseError parseerrormessagehandle(TSyntaxError se){ 1079 if (se.errortype == EErrorType.sphint){ 1080 this.getAncestorStmt().syntaxHints.add(se); 1081 }else 1082 this.getAncestorStmt().syntaxErrors.add(se); 1083 return EActionOnParseError.aopcontinue; 1084 } 1085 1086 public int parsestatement(TCustomSqlStatement pparentsql,boolean isparsetreeavailable){ 1087 return parsestatement(pparentsql,isparsetreeavailable,false); 1088 } 1089 1090 /** 1091 * Parse this statement. 1092 * @param pparentsql 1093 * @param isparsetreeavailable 1094 * @return parse result, zero means no syntax error found. 1095 */ 1096 public int parsestatement(TCustomSqlStatement pparentsql,boolean isparsetreeavailable, boolean onlyNeedRawParseTree){ 1097 int ret = 0; 1098 isparsed = false; 1099 if (!isparsetreeavailable){ 1100 ret = checksyntax(pparentsql); 1101 } 1102 if (ret == 0) 1103 { 1104 isparsed = true; 1105 if (!onlyNeedRawParseTree){ 1106 ret = doParseStatement(pparentsql); 1107 } 1108 }else if ((dbvendor == EDbVendor.dbvsybase)||(dbvendor == EDbVendor.dbvsybasease || dbvendor == EDbVendor.dbvsqlanywhere || dbvendor == EDbVendor.dbvsybaseiq)){ 1109 if ((this.rootNode != null)&& 1110 ((sqlstatementtype == ESqlStatementType.sstmssqlcreateprocedure) 1111 ||(sqlstatementtype == ESqlStatementType.sstmssqlcreatefunction) 1112 ||(sqlstatementtype == ESqlStatementType.sstcreatetrigger) 1113 )){ 1114 if (!onlyNeedRawParseTree){ 1115 doParseStatement(pparentsql); 1116 } 1117 1118 } 1119 } 1120 return ret; 1121 } 1122 1123 public boolean OracleStatementCanBeSeparatedByBeginEndPair(){ 1124 return ( 1125 1126 (this.sqlstatementtype == ESqlStatementType.sstplsql_createprocedure) 1127 //|| (this.sqlstatementtype == ESqlStatementType.sst_block_with_label) 1128 ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createfunction) 1129 ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createpackage) 1130 ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createtype_placeholder) 1131 ||(this.sqlstatementtype == ESqlStatementType.sstoraclecreatepackagebody) 1132 ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createtrigger) 1133// ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createtypebody) 1134// ||(this.sqlstatementtype == ESqlStatementType.sstplsql_tabletypedef) 1135 ||(this.sqlstatementtype == ESqlStatementType.sstplsql_varraytypedef) 1136 ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createprocedure) 1137 ||(this.sqlstatementtype == ESqlStatementType.sstplsql_execimmestmt) 1138 ||(this.sqlstatementtype == ESqlStatementType.sstoraclecreatelibrary) 1139 ); 1140 } 1141 1142 public boolean VerticaStatementCanBeSeparatedByBeginEndPair(){ 1143 return ( 1144 1145 (this.sqlstatementtype == ESqlStatementType.sstcreatefunction) 1146 ); 1147 } 1148 1149 public boolean isnzplsql(){ 1150 return ( 1151 (this.sqlstatementtype == ESqlStatementType.sstcreateprocedure) 1152 ); 1153 } 1154 1155 public boolean ispgplsql(){ 1156 return (this instanceof TCommonBlock) 1157 ||(this.sqlstatementtype == ESqlStatementType.sstcreateprocedure) 1158 ||(this.sqlstatementtype == ESqlStatementType.sstcreatefunction) 1159 ||(this.sqlstatementtype == ESqlStatementType.sstDoExecuteBlock) 1160 ; 1161 } 1162 1163 public boolean isGaussDBStoredProcedure(){ 1164 return (this instanceof TCommonBlock) 1165 ||(this.sqlstatementtype == ESqlStatementType.sstcreateprocedure) 1166 ||(this.sqlstatementtype == ESqlStatementType.sstcreatefunction) 1167 ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createpackage) 1168 ||(this.sqlstatementtype == ESqlStatementType.sstoraclecreatepackagebody) 1169 ||(this.sqlstatementtype == ESqlStatementType.sstDoExecuteBlock) 1170 ||(this.sqlstatementtype == ESqlStatementType.sstcreatetrigger) 1171 ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createtypebody) 1172 ; 1173 } 1174 1175 public boolean isdatabricksplsql(){ 1176 return (this instanceof TCommonBlock) 1177 ; 1178 } 1179 1180 public boolean isgreeplumplsql(){ 1181 return (this instanceof TCommonBlock) 1182 ||(this.sqlstatementtype == ESqlStatementType.sstcreateprocedure) 1183 ||(this.sqlstatementtype == ESqlStatementType.sstcreatefunction) 1184 ||(this.sqlstatementtype == ESqlStatementType.sstDoExecuteBlock) 1185 ; 1186 } 1187 1188 public boolean isathenaplsql(){ 1189 return (this instanceof TCommonBlock) 1190 ; 1191 } 1192 1193 public boolean isprestoplsql(){ 1194 return (this instanceof TCommonBlock) 1195 ; 1196 } 1197 1198 public boolean issnowflakeplsql(){ 1199 return ((this instanceof TCommonBlock) 1200 ||(this.sqlstatementtype == ESqlStatementType.sstcreateprocedure) 1201 ); 1202 } 1203 1204 public boolean isBigQueryplsql(){ 1205 return ((this instanceof TCommonBlock) 1206 ||(this.sqlstatementtype == ESqlStatementType.sstcreateprocedure) 1207 ); 1208 } 1209 1210 public boolean isverticaplsql(){ 1211 return ( 1212 (this.sqlstatementtype == ESqlStatementType.sstcreatefunction) 1213 ); 1214 } 1215 1216 public boolean isoracleplsql(){ 1217 return ( 1218 (this.sqlstatementtype == ESqlStatementType.sst_plsql_block) 1219 ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createprocedure) 1220 ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createfunction) 1221 ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createpackage) 1222 ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createtype_placeholder) 1223 ||(this.sqlstatementtype == ESqlStatementType.sstoraclecreatepackagebody) 1224 ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createtrigger) 1225 ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createtypebody) 1226 ||(this.sqlstatementtype == ESqlStatementType.sstplsql_tabletypedef) 1227 ||(this.sqlstatementtype == ESqlStatementType.sstplsql_varraytypedef) 1228 ||(this.sqlstatementtype == ESqlStatementType.sstplsql_createprocedure) 1229 ||(this.sqlstatementtype == ESqlStatementType.sstplsql_execimmestmt) 1230 ||(this.sqlstatementtype == ESqlStatementType.sstoraclecreatelibrary) 1231 ); 1232 } 1233 1234 int checksyntax(TCustomSqlStatement psql){ 1235 return dochecksyntax(psql); 1236 } 1237 1238 protected int dochecksyntax(TCustomSqlStatement psql){ 1239 int ret = -1; 1240 clear(); 1241 if (sourcetokenlist.size() == 0) return ret; 1242 1243// TCustomParser lcparser; 1244// lcparser = new TLzParserOracleSql(sourcetokenlist); 1245// lcparser.sql = this; 1246// ret = lcparser.yyparse(); 1247 1248 1249 if (((this.dbvendor == EDbVendor.dbvoracle)||(this.dbvendor == EDbVendor.dbvoceanbase))&&(this.isoracleplsql()&&(plsqlparser!=null)) 1250// || ((this.dbvendor == EDbVendor.dbvgaussdb) // gaussdb 中用 oracle plsql 写的存储过程,用 oracle plsql parser 来解析 1251// &&( 1252// ((this instanceof TCreateFunctionStmt)&&(((TCreateFunctionStmt)this).isGaussDBSpInOracle())) 1253// ||((this instanceof TCreateProcedureStmt)&&(((TCreateProcedureStmt)this).isGaussDBSpInOracle())) 1254// ||(this instanceof TPlsqlCreatePackage) 1255// ) 1256// ) 1257 ){ 1258 plsqlparser.sql = this; 1259// if (this.dbvendor == EDbVendor.dbvgaussdb){ 1260// // 原来用 gaussDB lexer tokenize 的 token 需要用 Oracle lexer 重新 tokenize 一边 1261// String sqlText=""; 1262// for(int k = 0;k<sourcetokenlist.size();k++){ 1263// sqlText = sqlText + sourcetokenlist.get(k).toString(); 1264// } 1265// // TODO, need to use singleton pattern to get a single instance of Oracle parser. 1266// TGSqlParser sqlParser = new TGSqlParser(EDbVendor.dbvoracle); 1267// // keep coordinate of the origin query 1268// long originalLineNo = sourcetokenlist.get(0).lineNo; 1269// long originalColumnNo = sourcetokenlist.get(0).columnNo; 1270// sqlParser.sqltext = TBaseType.stringBlock((int) originalLineNo - 1,(int) originalColumnNo - 1)+ sqlText;; 1271// 1272// int r = sqlParser.getrawsqlstatements(); 1273// sourcetokenlist.clear(); 1274// for(int k=0;k<sqlParser.sourcetokenlist.size();k++){ 1275// sourcetokenlist.add(sqlParser.sourcetokenlist.get(k)); 1276// } 1277// } 1278 1279 plsqlparser.sourcetokenlist = sourcetokenlist; 1280 1281 if ((this instanceof TCommonStoredProcedureSqlStatement) 1282 &&((TCommonStoredProcedureSqlStatement)this).isWrapped()){ 1283 // don't parse wrapped oracle plsql 1284 ret = 0; 1285 this.rootNode = this; 1286 }else { 1287 ret = plsqlparser.yyparse(); 1288 this.rootNode = plsqlparser.rootNode; 1289 } 1290 } 1291 else{ 1292 if ((this.sqlstatementtype == ESqlStatementType.sstExplain)&&(dbvendor != EDbVendor.dbvhana)){ 1293 // EXPLAIN PLAN ... FOR statement; only parse token after FOR keyword 1294 boolean isFoundStopToken = false; 1295 1296 for(int k=0;k<sourcetokenlist.size();k++){ 1297 TSourceToken st = sourcetokenlist.get(k); 1298 switch (dbvendor){ 1299 case dbvoracle: 1300 if (st.tokencode == TBaseType.rrw_for) { 1301 st.tokencode = TBaseType.sqlpluscmd; 1302 isFoundStopToken = true; 1303 } 1304 break; 1305 case dbvdameng: 1306 if (st.tokencode == TBaseType.rrw_for) { 1307 st.tokencode = TBaseType.sqlpluscmd; 1308 isFoundStopToken = true; 1309 } 1310 break; 1311 case dbvredshift: 1312 if (st.tokencode == TBaseType.rrw_explain){ 1313 st.tokencode = TBaseType.sqlpluscmd; 1314 TSourceToken nextst = st.nextSolidToken(); 1315 if (nextst.tokencode == TBaseType.rrw_redshift_verbose){ 1316 nextst.tokencode = TBaseType.sqlpluscmd; 1317 //System.out.println("Found verbose after explain"); 1318 } 1319 isFoundStopToken = true; 1320 } 1321 break; 1322 case dbvvertica: 1323 if ((st.tokencode == TBaseType.rrw_select) 1324 ||(st.tokencode == TBaseType.rrw_insert) 1325 ||(st.tokencode == TBaseType.rrw_update) 1326 ||(st.tokencode == TBaseType.rrw_merge) 1327 ) 1328 { 1329 isFoundStopToken = true; 1330 } 1331 break; 1332 case dbvclickhouse: 1333 case dbvmysql: 1334 case dbvoceanbase: 1335 case dbvsparksql: 1336 case dbvdatabricks: 1337 if ((st.tokencode == TBaseType.rrw_select) 1338 ||(st.tokencode == TBaseType.rrw_insert) 1339 ||(st.tokencode == TBaseType.rrw_update) 1340 ||(st.tokencode == TBaseType.rrw_delete) 1341 ||(st.tokencode == TBaseType.rrw_replace) 1342 ||(st.tokencode == TBaseType.rrw_with) 1343 ||(st.tokencode == TBaseType.rrw_create) 1344 ||(st.tokencode == '(') 1345 ) 1346 { 1347 isFoundStopToken = true; 1348 } 1349 break; 1350 case dbvpostgresql: 1351 if ((st.tokencode == TBaseType.rrw_select) 1352 ||(st.tokencode == TBaseType.rrw_insert) 1353 ||(st.tokencode == TBaseType.rrw_update) 1354 ||(st.tokencode == TBaseType.rrw_delete) 1355 ||(st.tokencode == TBaseType.rrw_replace) 1356 ||(st.tokencode == TBaseType.rrw_with) 1357 ||(st.tokencode == TBaseType.rrw_create) 1358 ||(st.tokencode == TBaseType.rrw_execute) 1359 ) 1360 { 1361 isFoundStopToken = true; 1362 }else if (st.tokencode == '('){ 1363 // Check if this '(' starts EXPLAIN options like (COSTS FALSE) 1364 // or a subquery like (SELECT ...) 1365 TSourceToken nextSolid = sourcetokenlist.nextsolidtoken(k, 1, false); 1366 if (nextSolid != null 1367 && nextSolid.tokencode != TBaseType.rrw_select 1368 && nextSolid.tokencode != TBaseType.rrw_insert 1369 && nextSolid.tokencode != TBaseType.rrw_update 1370 && nextSolid.tokencode != TBaseType.rrw_delete 1371 && nextSolid.tokencode != TBaseType.rrw_with){ 1372 // Options list: skip past closing ')' 1373 int depth = 1; 1374 st.tokencode = TBaseType.sqlpluscmd; 1375 for (k = k + 1; k < sourcetokenlist.size() && depth > 0; k++){ 1376 TSourceToken inner = sourcetokenlist.get(k); 1377 if (inner.tokencode == '(') depth++; 1378 else if (inner.tokencode == ')') depth--; 1379 inner.tokencode = TBaseType.sqlpluscmd; 1380 } 1381 k--; // adjust for loop increment 1382 }else{ 1383 isFoundStopToken = true; 1384 } 1385 } 1386 break; 1387 case dbvduckdb: 1388 if ((st.tokencode == TBaseType.rrw_select) 1389 ||(st.tokencode == TBaseType.rrw_insert) 1390 ||(st.tokencode == TBaseType.rrw_update) 1391 ||(st.tokencode == TBaseType.rrw_delete) 1392 ||(st.tokencode == TBaseType.rrw_replace) 1393 ||(st.tokencode == TBaseType.rrw_with) 1394 ||(st.tokencode == TBaseType.rrw_create) 1395 ||(st.tokencode == TBaseType.rrw_alter) 1396 ||(st.tokencode == TBaseType.rrw_merge) 1397 ||(st.tokencode == '(') 1398 ) 1399 { 1400 isFoundStopToken = true; 1401 } 1402 break; 1403 case dbvflink: 1404 // Flink EXPLAIN can have: EXPLAIN (options) stmt or EXPLAIN options stmt 1405 // Don't stop at '(' because it might be part of EXPLAIN (ESTIMATED_COST, ...) 1406 if ((st.tokencode == TBaseType.rrw_select) 1407 ||(st.tokencode == TBaseType.rrw_insert) 1408 ||(st.tokencode == TBaseType.rrw_update) 1409 ||(st.tokencode == TBaseType.rrw_delete) 1410 ||(st.tokencode == TBaseType.rrw_replace) 1411 ||(st.tokencode == TBaseType.rrw_with) 1412 ||(st.tokencode == TBaseType.rrw_create) 1413 ) 1414 { 1415 isFoundStopToken = true; 1416 } 1417 break; 1418 case dbvcouchbase: 1419 if (st.tokencode == TBaseType.rrw_explain){ 1420 st.tokencode = TBaseType.sqlpluscmd; 1421 isFoundStopToken = true; 1422 } 1423 break; 1424 case dbvpresto: 1425 case dbvathena: 1426 case dbvnetezza: 1427 if ((st.tokencode == TBaseType.rrw_select) 1428 ||(st.tokencode == TBaseType.rrw_insert) 1429 ||(st.tokencode == TBaseType.rrw_update) 1430 ||(st.tokencode == TBaseType.rrw_delete) 1431 ) 1432 { 1433 isFoundStopToken = true; 1434 } 1435 break; 1436 case dbvteradata: 1437 if ((st.tokencode == TBaseType.rrw_select) 1438 ||(st.tokencode == TBaseType.rrw_insert) 1439 ||(st.tokencode == TBaseType.rrw_update) 1440 ||(st.tokencode == TBaseType.rrw_delete) 1441 ||(st.tokencode == TBaseType.rrw_teradata_collect) 1442 ) 1443 { 1444 isFoundStopToken = true; 1445 } 1446 break; 1447 }//switch 1448 1449 if (isFoundStopToken) break; 1450 st.tokencode = TBaseType.sqlpluscmd; 1451 } 1452 }else if (this.sqlstatementtype == ESqlStatementType.sstProfile){ 1453 for(int k=0;k<sourcetokenlist.size();k++) { 1454 TSourceToken st = sourcetokenlist.get(k); 1455 if (dbvendor == EDbVendor.dbvvertica){ 1456 if ((st.tokencode == TBaseType.rrw_select) 1457 ||(st.tokencode == TBaseType.rrw_insert) 1458 ||(st.tokencode == TBaseType.rrw_update) 1459 ||(st.tokencode == TBaseType.rrw_merge) 1460 ) 1461 { 1462 break; 1463 } 1464 } 1465 st.tokencode = TBaseType.sqlpluscmd; 1466 } 1467 }else if (this.sqlstatementtype == ESqlStatementType.sstprepare){ 1468 if ((dbvendor == EDbVendor.dbvcouchbase)||(dbvendor == EDbVendor.dbvpresto)||(dbvendor == EDbVendor.dbvathena)){ 1469 int keywordCount = 0; 1470 for(int k=0;k<sourcetokenlist.size();k++) { 1471 TSourceToken st = sourcetokenlist.get(k); 1472 if (st.tokencode == TBaseType.rrw_prepare) 1473 { 1474 keywordCount++; 1475 }else if ((st.tokentype == ETokenType.ttkeyword) 1476 &&(st.tokencode != TBaseType.rrw_from) 1477 &&(st.tokencode != TBaseType.rrw_as)){ 1478 keywordCount++; 1479 if (keywordCount > 1) break; 1480 } 1481 st.tokencode = TBaseType.sqlpluscmd; 1482 } 1483 } 1484 } 1485 1486 if (parser == null){ 1487 // statement such as select/insert and etc inside plsql 1488 if (psql != null){ 1489 parser = psql.getTopStatement().parser; 1490 this.setParentStmt(psql); 1491 } 1492 // parser = new TParserOracleSql(null); 1493 //parser.lexer = new TLexerOracle(); 1494 //parser.lexer.delimiterchar = '/'; 1495 } 1496 parser.sql = this; 1497 parser.sourcetokenlist = sourcetokenlist; 1498 ret = parser.yyparse(); 1499 this.rootNode = parser.rootNode; 1500 } 1501 1502 if (ret == 0){ 1503 ret = syntaxErrors.size(); 1504 } 1505 // if (rootNode == null) { 1506 if (rootNode == null) { 1507 // EXPLAIN FOR CONNECTION has no inner statement to parse - this is valid 1508 if (this.sqlstatementtype == ESqlStatementType.sstExplain && isExplainForConnection()) { 1509 ret = 0; 1510 } else { 1511 ret = TBaseType.MSG_ERROR_NO_ROOT_NODE; 1512 // todo , uncomment next line when all sql statements in .y file was processed 1513 parseerrormessagehandle( new TSyntaxError("no root node",0,0,"no_root_node",EErrorType.sperror,TBaseType.MSG_ERROR_NO_ROOT_NODE,this,-1)); 1514 } 1515 } 1516 return ret; 1517 } 1518 1519 private boolean isExplainForConnection() { 1520 if (sourcetokenlist == null) return false; 1521 for (int i = 0; i < sourcetokenlist.size() - 1; i++) { 1522 TSourceToken st = sourcetokenlist.get(i); 1523 if (st.toString().equalsIgnoreCase("for")) { 1524 TSourceToken next = sourcetokenlist.nextsolidtoken(i, 1, false); 1525 if (next != null && next.toString().equalsIgnoreCase("connection")) { 1526 return true; 1527 } 1528 } 1529 } 1530 return false; 1531 } 1532 1533 public void clearError(){ 1534 syntaxErrors.clear(); 1535 syntaxHints.clear(); 1536 } 1537 1538 void clear(){ 1539 syntaxErrors.clear(); 1540 syntaxHints.clear(); 1541// todo all subclass should add super() 1542 } 1543 1544 public void setStmtScope(TStmtScope stmtScope) { 1545 this.stmtScope = stmtScope; 1546 } 1547 1548 public TStmtScope getStmtScope() { 1549 return stmtScope; 1550 } 1551 1552 /** 1553 * Original SQL fragment of this statement. 1554 * @return Original statement text. 1555 */ 1556 1557 /* 1558 public String toString(){ 1559 StringBuffer sb = new StringBuffer(""); 1560 for(int i=0; i<sourcetokenlist.size();i++){ 1561 sb.append(sourcetokenlist.get(i).toString()); 1562 } 1563 return sb.toString(); 1564 } 1565 */ 1566 protected TStmtScope stmtScope = null; 1567 void buildsql(){} 1568 public int doParseStatement(TCustomSqlStatement psql){ 1569 if (psql != null){ 1570 this.setParentStmt(psql); 1571 this.setFrameStack(psql.getFrameStack()); 1572 psql.stmtScope.incrementCurrentStmtIndex(); 1573 this.queryId = String.format("%s#stmt_%d_%s", psql.getQueryId(),psql.stmtScope.getCurrentStmtIndex(), this.sqlstatementtype); 1574 stmtScope = new TStmtScope(psql.stmtScope,this); 1575 // psql.statements.add(this); 1576 }else{ 1577 stmtScope = new TStmtScope(this); 1578 1579 // global scope 1580 this.getFrameStack().peek().getScope().incrementCurrentStmtIndex(); 1581 this.queryId = String.format("stmt_%d_%s",this.getFrameStack().peek().getScope().getCurrentStmtIndex(), this.sqlstatementtype); 1582 } 1583 1584 if ((this.getStartToken() == null)&&(rootNode != null)){ 1585 this.setStartToken(rootNode.getStartToken()); 1586 } 1587 if ((this.getEndToken() == null)&&(rootNode != null)){ 1588 this.setEndToken(rootNode.getEndToken()); 1589 } 1590 1591 if(this.getGsqlparser() == null){ 1592 if (rootNode != null){ 1593 this.setGsqlparser(rootNode.getGsqlparser()); 1594 } 1595 } 1596 return 0; 1597 } 1598 1599 void addtokentolist(TSourceToken st){ 1600 st.stmt = this; 1601 sourcetokenlist.add(st); 1602 } 1603 1604 public TTable analyzeTablename(TObjectName tableName){ 1605 TTable lcTable = new TTable(); 1606 lcTable.setTableType(ETableSource.objectname); 1607 lcTable.setStartToken(tableName.getStartToken()); 1608 lcTable.setEndToken(tableName.getEndToken()); 1609 lcTable.setGsqlparser(this.getGsqlparser()); 1610 lcTable.setTableName(tableName); 1611 1612 tables.addTable(lcTable); 1613 return lcTable; 1614 } 1615 1616 protected boolean isTableACTE(TTable pTable){ 1617 boolean lcResult = false; 1618 TCTEList cteList1 = getCteList(); 1619 if (cteList1 == null){ 1620 TCustomSqlStatement lcStmt = getParentStmt(); 1621 while (lcStmt != null){ 1622 if (lcStmt.getCteList() != null){ 1623 cteList1 = lcStmt.getCteList(); 1624 break; 1625 }else { 1626 lcStmt = lcStmt.getParentStmt(); 1627 } 1628 } 1629 } 1630 if (cteList1 == null) return false; 1631 // TCTE lcCTE = cteList1.cteNames.get(TBaseType.getTextWithoutQuoted(pTable.toString()).toUpperCase()); 1632 if (pTable.toString() == null) return false; 1633 1634 int searchPos = pTable.getStartToken().posinlist; 1635 if (this.getCteIncludeThisStmt() != null){ 1636 searchPos = this.getCteIncludeThisStmt().getStartToken().posinlist; 1637 } 1638 TCTE lcCTE = cteList1.searchCTEByName(TBaseType.getTextWithoutQuoted(pTable.toString()).toUpperCase(),searchPos); 1639 if ( lcCTE != null ){ 1640 if (pTable.setCTE(lcCTE)){ 1641 pTable.setCTEName(true); 1642 lcResult = true; 1643 } 1644 } 1645// for (int i=0;i<cteList1.size();i++){ 1646// lcCTE = cteList1.getCTE(i); 1647// if (TBaseType.getTextWithoutQuoted(lcCTE.getTableName().toString()).equalsIgnoreCase(TBaseType.getTextWithoutQuoted(pTable.toString()))){ 1648// pTable.setCTEName(true); 1649// pTable.setCTE(lcCTE); 1650// lcResult = true; 1651// break; 1652// } 1653// } 1654 1655 return lcResult; 1656 1657 } 1658 1659 public TTable findTable(ETableEffectType[] tableEffectTypes){ 1660 TTable lcResult = null; 1661 for(int i=0;i<tables.size();i++){ 1662 for(int j=0;j<tableEffectTypes.length;j++){ 1663 if (tables.getTable(i).getEffectType() == tableEffectTypes[j]){ 1664 lcResult = tables.getTable(i); 1665 return lcResult; 1666 } 1667 } 1668 } 1669 return lcResult; 1670 } 1671 public void addToTables(TTable pTable){ 1672 tables.addTable(pTable); 1673 if (isTableACTE(pTable)) return; 1674 1675 if (pTable.getTableName() == null) return; 1676 if (pTable.getTableName().getTableToken() == null) return; 1677 if ((pTable.getTableName().getTableString().toString().equalsIgnoreCase("inserted"))||(pTable.getTableName().getTableString().toString().equalsIgnoreCase("deleted"))){ 1678 if ((getAncestorStmt().sqlstatementtype == ESqlStatementType.sstcreatetrigger) 1679 ||(getAncestorStmt().sqlstatementtype == ESqlStatementType.sstmssqlaltertrigger)){ 1680 //pTable.setLinkTable(true); 1681 ETableEffectType[] effectTypes = new ETableEffectType[]{ 1682 ETableEffectType.tetTriggerOn,ETableEffectType.tetTriggerInsert,ETableEffectType.tetTriggerDelete,ETableEffectType.tetTriggerUpdate,ETableEffectType.tetTriggerInsteadOf 1683 }; 1684 pTable.setLinkTable(getAncestorStmt().findTable(effectTypes)); 1685 } 1686 } 1687 1688 } 1689 1690 public TJoin analyzeTableOrJoin(TFromTable pfromTable){ 1691 TFromTable lcFromTable = pfromTable; 1692 TJoin lcJoin; 1693 TTable lcTable; 1694 1695 if (lcFromTable.getFromtableType() != ETableSource.join){ 1696 lcJoin = new TJoin(); 1697 lcTable = analyzeFromTable(lcFromTable,true); 1698 lcTable.setEffectType(ETableEffectType.tetSelect); 1699 lcJoin.setTable(lcTable); 1700 lcJoin.setStartToken(lcJoin.getTable().getStartToken()); 1701 lcJoin.setEndToken(lcJoin.getTable().getEndToken()); 1702 lcJoin.setGsqlparser(getGsqlparser()); 1703 this.fromSourceTable = lcTable; 1704 this.getRelations().add(lcTable); 1705 }else{ 1706 this.fromSourceJoin = lcFromTable.getJoinExpr(); 1707 1708 this.fromSourceTable = new TTable(); 1709 this.fromSourceTable.setTableType(ETableSource.join); 1710 this.fromSourceTable.setAliasClause(lcFromTable.getJoinExpr().getAliasClause()); 1711 this.fromSourceTable.setStartToken(lcFromTable.getStartToken()); 1712 this.fromSourceTable.setEndToken(lcFromTable.getEndToken()); 1713 this.fromSourceTable.setGsqlparser(lcFromTable.getGsqlparser()); 1714 this.fromSourceTable.setJoinExpr(this.fromSourceJoin); 1715 this.getRelations().add(this.fromSourceTable); 1716 1717 lcJoin = analyzeJoin(lcFromTable.getJoinExpr(),null,true); 1718 lcJoin.doParse(this, ESqlClause.join); 1719 1720 if (lcFromTable.getLateralViewList() != null){ 1721 for(TLateralView lateralView:lcFromTable.getLateralViewList()){ 1722 TTable t = lateralView.createATable(this); 1723 addToTables(t); 1724 this.relations.add(t); 1725 } 1726 } 1727 } 1728 1729 return lcJoin; 1730 } 1731 1732 public TTable analyzeFromTable(TFromTable pfromTable, Boolean addToTableList){ 1733 return analyzeFromTable(pfromTable,addToTableList,ESqlClause.unknown); 1734 } 1735 1736 public TTable analyzeFromTable(TFromTable pfromTable, Boolean addToTableList, ESqlClause pLocation){ 1737 TTable lcTable = new TTable(); 1738 lcTable.setTableType(pfromTable.getFromtableType()); 1739 lcTable.setAliasClause(pfromTable.getAliasClause()); 1740 lcTable.setStartToken(pfromTable.getStartToken()); 1741 lcTable.setEndToken(pfromTable.getEndToken()); 1742 lcTable.setGsqlparser(pfromTable.getGsqlparser()); 1743 lcTable.setTableHintList(pfromTable.getTableHintList()); 1744 lcTable.setTableSample(pfromTable.getTableSample()); 1745 lcTable.setLateralViewList(pfromTable.getLateralViewList()); 1746 lcTable.setTableProperties(pfromTable.getTableProperties()); 1747 lcTable.setPivotedTable(pfromTable.getPivotedTable()); 1748 lcTable.setParenthesisCount(pfromTable.getParenthesisCount()); 1749 lcTable.setParenthesisAfterAliasCount(pfromTable.getParenthesisAfterAliasCount()); 1750 lcTable.setTableKeyword(pfromTable.isTableKeyword()); 1751 lcTable.setOnlyKeyword(pfromTable.isOnlyKeyword()); 1752 lcTable.setArrayIndexAlias(pfromTable.getArrayIndexAlias()); 1753 lcTable.setSuperUnpivot(pfromTable.isSuperUnpivot()); 1754 lcTable.setFlashback(pfromTable.getFlashback()); 1755 lcTable.setPxGranule(pfromTable.getPxGranule()); 1756 lcTable.setTimeTravelClause(pfromTable.getTimeTravelClause()); 1757 //lcTable.setPartitionClause(pfromTable.getPartitionClause()); 1758 1759 if(getFrameStack().firstElement() != null){ 1760 TFrame stackFrame = getFrameStack().firstElement(); 1761 TGlobalScope globalScope = (TGlobalScope)stackFrame.getScope(); 1762 lcTable.setSqlEnv(globalScope.getSqlEnv()); 1763 } 1764 1765 switch(lcTable.getTableType()){ 1766 case objectname:{ 1767 // tables.addTableByTableRefernce(pfromTable.getTableObjectName()); 1768 boolean insertedInTrigger = false; 1769 if (getTopStatement().sqlstatementtype == ESqlStatementType.sstcreatetrigger){ 1770 insertedInTrigger = (pfromTable.getTableObjectName().toString().compareToIgnoreCase("inserted")==0); 1771 } 1772 1773 if (insertedInTrigger){ 1774 // change table name from inserted to onTable name in create trigger 1775 lcTable.setTableName(((TCreateTriggerStmt)getTopStatement()).getOnTable().getTableName()); 1776 //lcTable.setLinkTable(true); 1777 lcTable.setLinkTable(((TCreateTriggerStmt)getTopStatement()).getOnTable()); 1778 1779 }else{ 1780 lcTable.setTableName(pfromTable.getTableObjectName()); 1781 lcTable.getTableName().setSqlEnv(getSqlEnv()); 1782 1783// if (getSqlEnv().getDefaultCatalogName() != null){ 1784// if (lcTable.getTableName().getDatabaseToken() == null){ 1785// lcTable.getTableName().setDatabaseToken(new TSourceToken(getSqlEnv().getDefaultCatalogName()),true); 1786// } 1787// } 1788 1789// if ((lcTable.getTableName().getSchemaToken() == null)&&(TSQLEnv.supportSchema(this.dbvendor))){ 1790// // let find schema name for this table in env 1791// TSQLTable t = getSqlEnv().searchTable(".."+lcTable.getFullName()); 1792// if (t != null){ 1793// TSQLSchema s = t.getSchema(); 1794// if (s != null){ 1795// lcTable.getTableName().setSchemaToken(new TSourceToken(s.getName()),true); 1796// } 1797// } 1798// } 1799 1800 } 1801 // let's check is it cte name or ordinary table name 1802 TCTEList lcCteList = searchCTEList(false); 1803 TCTE lcCte = null; 1804 if (lcCteList != null){ 1805 for(int i=0;i<lcCteList.size();i++){ 1806 lcCte = lcCteList.getCTE(i); 1807 if (lcCte.getTableName().toString().compareToIgnoreCase(TBaseType.getTextWithoutQuoted(lcTable.getTableName().toString()))==0){ 1808 // this is cte name 1809 if (lcTable.setCTE(lcCte)){ 1810 lcTable.setCTEName(true); 1811 lcTable.setCteColomnReferences(lcCte.getColumnList()); 1812 break; 1813 } 1814 } 1815 } 1816 } 1817 1818 break; 1819 } 1820 case tableExpr:{ 1821 ESqlClause location = ESqlClause.tableExpr; //ESqlClause.resultColumn; 1822 if (sqlstatementtype == ESqlStatementType.sstinsert ){ 1823 // change location here 1824 } 1825 lcTable.setTableExpr(pfromTable.getTableExpr()); 1826 lcTable.getTableExpr().doParse(this,location); 1827 // teradata: SELECT table1.* FROM table(strtok_split_to_table(1, 'dm-calcite-raven/td/bq', '-') RETURNS (outkey integer, tokennum integer, token varchar(20)) ) as table1; 1828 // RETURNS (outkey integer, tokennum integer, token varchar(20)) 1829 lcTable.setColumnDefinitions(pfromTable.getColumnDefinitions()); 1830 // Teradata table function HASH BY and LOCAL ORDER BY clauses 1831 lcTable.setHashByClause(pfromTable.getHashByClause()); 1832 lcTable.setLocalOrderBy(pfromTable.getLocalOrderBy()); 1833 break; 1834 } 1835 case subquery:{ 1836// if (pfromTable.getSubquerynode().isHiveFromQuery()){ 1837// THiveFromQuery fromQuery = new THiveFromQuery(dbvendor); 1838// lcTable.setHiveFromQuery(fromQuery); 1839// fromQuery.rootNode = pfromTable.getSubquerynode(); 1840// fromQuery.setStartToken(pfromTable.getSubquerynode()); 1841// fromQuery.setEndToken(pfromTable.getSubquerynode()); 1842// fromQuery.setLabelName(this.labelName); 1843// fromQuery.doParseStatement(this); 1844// }else{ 1845// lcTable.subquery = new TSelectSqlStatement(dbvendor); 1846// lcTable.subquery.rootNode = pfromTable.getSubquerynode(); 1847// lcTable.subquery.setLocation(ESqlClause.elTable); 1848// //lcTable.subquery.resultColumnList = ((TSelectSqlNode)lcTable.subquery.rootNode).getResultColumnList(); 1849// lcTable.subquery.doParseStatement(this); 1850// } 1851 1852 lcTable.subquery = new TSelectSqlStatement(dbvendor); 1853 lcTable.subquery.rootNode = pfromTable.getSubquerynode(); 1854 if (pLocation == ESqlClause.unknown){ 1855 lcTable.subquery.setLocation(ESqlClause.elTable); 1856 }else{ 1857 lcTable.subquery.setLocation(pLocation); 1858 } 1859 //lcTable.subquery.resultColumnList = ((TSelectSqlNode)lcTable.subquery.rootNode).getResultColumnList(); 1860 lcTable.subquery.doParseStatement(this); 1861 1862 break; 1863 } 1864 case function:{ 1865 ESqlClause location = ESqlClause.tableFunction;// resultColumn; 1866 if (sqlstatementtype == ESqlStatementType.sstinsert ){ 1867 // change location here 1868 } 1869 lcTable.setFuncCall(pfromTable.getFuncCall()); 1870 lcTable.getFuncCall().doParse(this,location); 1871 break; 1872 } 1873 case containsTable:{ 1874 ESqlClause location = ESqlClause.containsTable;//resultColumn; 1875 if (sqlstatementtype == ESqlStatementType.sstinsert ){ 1876 // change location here 1877 } 1878 lcTable.setContainsTable(pfromTable.getContainsTable()); 1879 lcTable.getContainsTable().doParse(this,location); 1880 break; 1881 } 1882 1883 case openrowset:{ 1884 ESqlClause location = ESqlClause.openrowset;//resultColumn; 1885 if (sqlstatementtype == ESqlStatementType.sstinsert ){ 1886 // change location here 1887 } 1888 lcTable.setOpenRowSet(pfromTable.getOpenRowSet()); 1889 lcTable.getOpenRowSet().doParse(this,location); 1890 break; 1891 } 1892 1893 case openxml:{ 1894 ESqlClause location = ESqlClause.openxml;//resultColumn; 1895 if (sqlstatementtype == ESqlStatementType.sstinsert ){ 1896 // change location here 1897 } 1898 lcTable.setOpenXML(pfromTable.getOpenXML()); 1899 lcTable.getOpenXML().doParse(this,location); 1900 break; 1901 } 1902 1903 case opendatasource:{ 1904 ESqlClause location = ESqlClause.opendatasource;//resultColumn; 1905 if (sqlstatementtype == ESqlStatementType.sstinsert ){ 1906 // change location here 1907 } 1908 lcTable.setOpenDatasource(pfromTable.getOpenDatasource()); 1909 lcTable.getOpenDatasource().doParse(this,location); 1910 break; 1911 } 1912 1913 case openquery:{ 1914 ESqlClause location = ESqlClause.openquery;//resultColumn; 1915 if (sqlstatementtype == ESqlStatementType.sstinsert ){ 1916 // change location here 1917 } 1918 lcTable.setOpenquery(pfromTable.getOpenQuery()); 1919 lcTable.getOpenquery().doParse(this,location); 1920 lcTable.setSubquery(lcTable.getOpenquery().getSubquery()); 1921 break; 1922 } 1923 1924 case datachangeTable:{ 1925 ESqlClause location = ESqlClause.datachangeTable;//resultColumn; 1926 if (sqlstatementtype == ESqlStatementType.sstinsert ){ 1927 // change location here 1928 } 1929 lcTable.setDatachangeTable(pfromTable.getDatachangeTable()); 1930 lcTable.getDatachangeTable().doParse(this,location); 1931 break; 1932 } 1933 case rowList:{ 1934 ESqlClause location = ESqlClause.rowList;//resultColumn; 1935 lcTable.setValueClause(pfromTable.getValueClause()); 1936 lcTable.getValueClause().doParse(this,location); 1937 break; 1938 } 1939 case pivoted_table:{ 1940 ESqlClause location = ESqlClause.pivoted_table;//resultColumn; 1941 lcTable.getPivotedTable().doParse(this,location); 1942 addToTableList = false; 1943 targetTable = lcTable; 1944 break; 1945 } 1946 case xmltable:{ 1947 ESqlClause location = ESqlClause.xmltable;//resultColumn; 1948 lcTable.setXmlTable(pfromTable.getXmlTable()); 1949 lcTable.getXmlTable().doParse(this,location); 1950 break; 1951 } 1952 1953 case informixOuter:{ 1954 ESqlClause location = ESqlClause.outerTable;//resultColumn; 1955 lcTable.setOuterClause(pfromTable.getOuterClause()); 1956 lcTable.getOuterClause().doParse(this,location); 1957 break; 1958 } 1959 1960 case table_ref_list:{ 1961 lcTable.setFromTableList(pfromTable.getFromTableList()); 1962 break; 1963 } 1964// case hiveFromQuery:{ 1965// THiveFromQuery fromQuery = new THiveFromQuery(EDbVendor.dbvhive); 1966// fromQuery.rootNode = pfromTable.getFromQuerySqlNode(); 1967// fromQuery.doParseStatement(this); 1968// lcTable.setHiveFromQuery(fromQuery); 1969// break; 1970// } 1971 case output_merge:{ 1972 TMergeSqlStatement outputMerge = new TMergeSqlStatement(EDbVendor.dbvmssql); 1973 outputMerge.rootNode = pfromTable.getMergeSqlNode(); 1974 outputMerge.doParseStatement(this); 1975 lcTable.setOutputMerge(outputMerge); 1976 break; 1977 } 1978 case td_unpivot:{ 1979 // Set the TD_UNPIVOT output table before doParse so that VALUE_COLUMNS and 1980 // UNPIVOT_COLUMN can be linked to it (they are output columns of TD_UNPIVOT) 1981 pfromTable.getTdUnpivot().setTdUnpivotOutputTable(lcTable); 1982 pfromTable.getTdUnpivot().doParse(this,ESqlClause.tdUnPivot); 1983 lcTable.setTdUnpivot(pfromTable.getTdUnpivot()); 1984 break; 1985 } 1986 case unnest:{ 1987 pfromTable.getUnnestClause().doParse(this,ESqlClause.elTable); 1988 lcTable.setUnnestClause(pfromTable.getUnnestClause()); 1989 if (lcTable.getAliasClause() != null){ 1990 if (lcTable.getAliasClause().getColumns() != null){ 1991 for(TObjectName pColumn:lcTable.getAliasClause().getColumns()){ 1992 lcTable.getLinkedColumns().addObjectName(pColumn); 1993 pColumn.setSourceTable(lcTable); 1994 } 1995 }else if (lcTable.getAliasClause().getAliasName() != null){ 1996// SELECT * 1997// FROM UNNEST(['foo', 'bar', 'baz', 'qux', 'corge', 'garply', 'waldo', 'fred']) AS element 1998// WITH OFFSET AS offset 1999 2000 // add element as column of unnest table. 2001 TObjectName newColumn = TObjectName.createObjectName(this.dbvendor,EDbObjectType.column,lcTable.getAliasClause().getAliasName().getStartToken()); 2002 lcTable.getLinkedColumns().addObjectName(newColumn); 2003 newColumn.setSourceTable(lcTable); 2004 } 2005 } 2006 2007 if (lcTable.getUnnestClause().getWithOffset() != null){ 2008 if (lcTable.getUnnestClause().getWithOffsetAlais() != null){ 2009 // with offset as offsetAlias 2010 TAliasClause aliasClause = lcTable.getUnnestClause().getWithOffsetAlais(); 2011 if (aliasClause.getAliasName() != null){ 2012 TObjectName newColumn = TObjectName.createObjectName(this.dbvendor,EDbObjectType.column,aliasClause.getAliasName().getStartToken()); 2013 lcTable.getLinkedColumns().addObjectName(newColumn); 2014 newColumn.setSourceTable(lcTable); 2015 } 2016 }else{ 2017 // with offset 2018 TObjectName newColumn = TObjectName.createObjectName(this.dbvendor,EDbObjectType.column,new TSourceToken("offset")); 2019 lcTable.getLinkedColumns().addObjectName(newColumn); 2020 newColumn.setSourceTable(lcTable); 2021 } 2022 } 2023 2024 // link columns in the select list to unnest() 2025 // select emp_id,name,state,city,zipcode from `absolute-runner-302907.gudu_sqlflow.ADDRESS_NESTED`, UNNEST(address) 2026 if (lcTable.getUnnestClause().getDerivedColumnList() != null){ 2027 TObjectNameList derivedColumns = lcTable.getUnnestClause().getDerivedColumnList(); 2028 for(int i=0;i<derivedColumns.size();i++){ 2029 //System.out.println(derivedColumns.getObjectName(i).toString()); 2030 lcTable.getLinkedColumns().addObjectName(derivedColumns.getObjectName(i)); 2031 } 2032 } 2033 2034 break; 2035 } 2036 case jsonTable:{ 2037 ESqlClause location = ESqlClause.jsonTable;//resultColumn; 2038 lcTable.setJsonTable(pfromTable.getJsonTable()); 2039 lcTable.getJsonTable().doParse(this,location); 2040 break; 2041 } 2042 case externalTable: 2043 lcTable.setTableName(pfromTable.getTableObjectName()); 2044 lcTable.setColumnDefinitions(pfromTable.getColumnDefinitions()); 2045 lcTable.getColumnDefinitions().doParse(this,pLocation); 2046 lcTable.setTableType(ETableSource.externalTable); // tableType is reset in setTableName() method, so we reset it here 2047 break; 2048 case caseJoin: 2049 lcTable.setCaseJoin(pfromTable.getCaseJoin()); 2050 lcTable.getCaseJoin().doParse(this,pLocation); 2051 break; 2052 case stageReference: 2053 lcTable.setStageReference(pfromTable.getStageReference()); 2054 lcTable.getStageReference().doParse(this,pLocation); 2055 2056 lcTable.setTableName(lcTable.getStageReference().getStageName()); 2057 lcTable.setTableType(ETableSource.stageReference); 2058 lcTable.getTableName().setSqlEnv(getSqlEnv()); 2059 2060 break; 2061 2062 }//switch 2063 2064// if (pfromTable.getPivotClause() != null){ 2065// lcTable.setPivotClause(pfromTable.getPivotClause()); 2066// lcTable.getPivotClause().doParse(this,ESqlClause.resultColumn); 2067// } 2068 2069 lcTable.setPartitionExtensionClause(pfromTable.getPartitionExtensionClause()); 2070 2071 //tables.addTable(lcTable); 2072 if (addToTableList) { 2073 addToTables(lcTable); 2074 } 2075 2076 if (lcTable.getTableHintList() != null){ 2077 for(int i=0;i<lcTable.getTableHintList().size();i++){ 2078 TTableHint hint = lcTable.getTableHintList().getElement(i); 2079 hint.setOwnerTable(lcTable); 2080 hint.doParse(this,ESqlClause.tableHint); 2081 } 2082 } 2083 2084 if (lcTable.getLateralViewList() != null){ 2085 for(TLateralView lateralView:lcTable.getLateralViewList()){ 2086 TTable t = lateralView.createATable(this); 2087 addToTables(t); 2088 this.relations.add(t); 2089 } 2090 } 2091 2092 if (lcTable.getAliasClause() != null){ 2093 if (lcTable.getAliasClause().toString().equalsIgnoreCase("and")){ 2094 // end keyword can't be alias name 2095 TSourceToken st1 = lcTable.getAliasClause().getStartToken(); 2096 TSyntaxError err = new TSyntaxError(st1.toString() 2097 ,st1.lineNo,st1.columnNo 2098 ,String.format("AND keyword can't be table alias") 2099 ,EErrorType.sperror 2100 ,TBaseType.MSG_ERROR_AND_KEYWORD_CANT_USED_AS_TABLE_ALIAS 2101 ,this,st1.posinlist); 2102 this.parseerrormessagehandle( err); 2103 2104 } 2105 } 2106 2107 return lcTable; 2108 } 2109 2110 public TJoin analyzeJoin(TJoinExpr pJoinExpr,TJoin pJoin,Boolean isSub){ 2111 TJoin retval = pJoin; 2112 TJoinItem lcJoinItem = null ; 2113 2114 if (pJoinExpr == null) {return retval;} 2115 2116 if (pJoinExpr.getJointype() == EJoinType.nested) 2117 { 2118 if (isSub) 2119 { 2120 if (retval == null) { // top level, left side is a join 2121 retval = new TJoin(); 2122 retval.setStartToken(pJoinExpr.getStartToken()); 2123 retval.setEndToken(pJoinExpr.getEndToken()); 2124 } 2125 2126 pJoinExpr.setJointype(pJoinExpr.original_jontype); 2127 retval.setJoin(analyzeJoin(pJoinExpr,null,true)); 2128 //retval =analyzeJoin(pJoinExpr,null,true); 2129 retval.setKind(TBaseType.join_source_join); 2130 retval.getJoin().setAliasClause(pJoinExpr.getAliasClause()); 2131 retval.getJoin().setWithParen(true); 2132 retval.getJoin().setNestedParen(pJoinExpr.getNestedParen()); 2133 } 2134 else 2135 { 2136 if (retval == null) 2137 { 2138 retval = new TJoin(); 2139 retval.setStartToken(pJoinExpr.getStartToken()); 2140 retval.setEndToken(pJoinExpr.getEndToken()); 2141 retval.setGsqlparser(this.getGsqlparser()); 2142 } 2143 else 2144 { 2145 } 2146 pJoinExpr.setJointype(pJoinExpr.original_jontype); 2147 retval = analyzeJoin(pJoinExpr,retval,false); 2148 //retval.setJoin(analyzeJoin(pJoinExpr,retval,false)); 2149 //retval = analyzeJoin(pJoinExpr,retval,false); 2150 //retval.setKind(TBaseType.join_source_join); 2151 //retval.setKind(TBaseType.join_source_table); 2152 //retval.setAliasClause(pJoinExpr.getAliasClause()); 2153 retval.setAliasClause(pJoinExpr.getAliasClause()); 2154 retval.setWithParen(true); 2155 retval.setNestedParen(pJoinExpr.getNestedParen()); 2156 } 2157 return retval; 2158 } 2159 2160 if (pJoinExpr.getLeftOperand().getFromtableType() != ETableSource.join){ 2161 if (retval == null) { 2162 retval = new TJoin(); 2163 retval.setStartToken(pJoinExpr.getStartToken()); 2164 retval.setEndToken(pJoinExpr.getEndToken()); 2165 retval.setGsqlparser(this.getGsqlparser()); 2166 2167 // retval.setStartToken(pJoinExpr.getLeftOperand().getStartToken()); 2168 // retval.setEndToken(pJoinExpr.getLeftOperand().getEndToken()); 2169 } 2170 TTable lcTable = analyzeFromTable(pJoinExpr.getLeftOperand(),true,ESqlClause.join); 2171 lcTable.setEffectType(ETableEffectType.tetSelect); 2172 retval.setTable(lcTable); 2173 //retval.joinTable.OwnerJoin = result; 2174 retval.setKind(TBaseType.join_source_table); 2175 pJoinExpr.setLeftTable(lcTable); 2176 }else{ 2177 TJoinExpr lcJoinItemJoinExpr = pJoinExpr.getLeftOperand().getJoinExpr(); 2178 //if (lcJoinItemJoinExpr.getJointype() == TBaseType.join_nested){ 2179 // lcJoinItemJoinExpr.setJointype(lcJoinItemJoinExpr.original_jontype); 2180 //} 2181 2182 if (retval != null) { 2183 retval = analyzeJoin(lcJoinItemJoinExpr,retval,true); 2184 } else { 2185 retval = analyzeJoin(lcJoinItemJoinExpr,retval,isSub); 2186 } 2187 retval.setStartToken(lcJoinItemJoinExpr.getStartToken()); 2188 retval.setEndToken(lcJoinItemJoinExpr.getEndToken()); 2189 2190 2191 TTable lcTable = new TTable(); 2192 lcTable.setTableType(pJoinExpr.getLeftOperand().getFromtableType()); 2193 lcTable.setAliasClause(lcJoinItemJoinExpr.getAliasClause()); 2194 lcTable.setStartToken(lcJoinItemJoinExpr.getStartToken()); 2195 lcTable.setEndToken(lcJoinItemJoinExpr.getEndToken()); 2196 pJoinExpr.setLeftTable(lcTable); 2197 lcTable.setJoinExpr(lcJoinItemJoinExpr); 2198 } 2199 2200 if (pJoinExpr.getRightOperand().getFromtableType() != ETableSource.join){ 2201 if (retval != null) 2202 { 2203 lcJoinItem = new TJoinItem(); 2204 TTable lcTable = analyzeFromTable(pJoinExpr.getRightOperand(),true,ESqlClause.join); 2205 lcTable.setEffectType(ETableEffectType.tetSelect); 2206 lcJoinItem.setTable(lcTable); 2207 lcJoinItem.setStartToken(lcJoinItem.getTable().getStartToken()); 2208 lcJoinItem.setEndToken(lcJoinItem.getTable().getEndToken()); 2209 // lcJoinItem.JoinItemTable.OwnerJoinItem := lcJoinItem; 2210 lcJoinItem.setKind(TBaseType.join_source_table); 2211 retval.getJoinItems().addJoinItem(lcJoinItem); 2212 pJoinExpr.setRightTable(lcTable); 2213 } 2214 }else{ 2215 if (retval != null) 2216 { 2217 lcJoinItem = new TJoinItem(); 2218 lcJoinItem.setKind(TBaseType.join_source_join); 2219 TJoinExpr lcJoinItemJoinExpr = pJoinExpr.getRightOperand().getJoinExpr(); 2220 //if (lcJoinItemJoinExpr.getJointype() == TBaseType.join_nested){ 2221 // lcJoinItemJoinExpr.setJointype(lcJoinItemJoinExpr.original_jontype); 2222 //} 2223 lcJoinItem.setJoin(analyzeJoin(pJoinExpr.getRightOperand().getJoinExpr(),null,false)); 2224 lcJoinItem.getJoin().setAliasClause(lcJoinItemJoinExpr.getAliasClause()); 2225 lcJoinItem.setStartToken(lcJoinItem.getJoin().getStartToken()); 2226 lcJoinItem.setEndToken(lcJoinItem.getJoin().getEndToken()); 2227 retval.getJoinItems().addJoinItem(lcJoinItem); 2228 2229 TTable lcTable = new TTable(); 2230 lcTable.setTableType(pJoinExpr.getRightOperand().getFromtableType()); 2231 lcTable.setAliasClause(lcJoinItemJoinExpr.getAliasClause()); 2232 lcTable.setStartToken(lcJoinItemJoinExpr.getStartToken()); 2233 lcTable.setEndToken(lcJoinItemJoinExpr.getEndToken()); 2234 pJoinExpr.setRightTable(lcTable); 2235 lcTable.setJoinExpr(lcJoinItemJoinExpr); 2236 } 2237 } 2238 2239 if (lcJoinItem == null) return retval; 2240 2241 lcJoinItem.setJoinType(pJoinExpr.getJointype()); 2242 lcJoinItem.setUsingColumns(pJoinExpr.usingColumns); 2243 if ((lcJoinItem.getUsingColumns() != null) && (tables.size()>1)){ 2244 TObjectName crf ; 2245 for (int i=0;i<lcJoinItem.getUsingColumns().size();i++){ 2246 crf = lcJoinItem.getUsingColumns().getObjectName(i); 2247 // link this column to last 2 tables 2248 tables.getTable(tables.size()-1).getObjectNameReferences().addObjectName(crf); 2249 tables.getTable(tables.size()-2).getObjectNameReferences().addObjectName(crf); 2250 2251 tables.getTable(tables.size()-1).getLinkedColumns().addObjectName(crf); 2252 crf.setSourceTable(tables.getTable(tables.size()-1)); 2253 tables.getTable(tables.size()-2).getLinkedColumns().addObjectName(crf); 2254 crf.setSourceTable(tables.getTable(tables.size()-2)); 2255 2256 } 2257 lcJoinItem.setEndToken(lcJoinItem.getUsingColumns().getEndToken()); 2258 } 2259 lcJoinItem.setOnCondition(pJoinExpr.onCondition); 2260 if (lcJoinItem.getOnCondition() != null) 2261 { 2262 lcJoinItem.getOnCondition().doParse(this,ESqlClause.joinCondition); 2263 lcJoinItem.setEndToken(lcJoinItem.getOnCondition().getEndToken()); 2264 } 2265 2266 2267 return retval; 2268 } 2269 2270 public boolean locateVariableOrParameter(TObjectName cr){ 2271 return locateVariableOrParameter(cr,false); 2272 } 2273 2274 public boolean locateVariableOrParameter(TObjectName cr, boolean checkVariableDeclaredInProcedure){ 2275 boolean ret = false; 2276 if (cr.getDbObjectType() == EDbObjectType.variable) return true; 2277 if (cr.toString().equalsIgnoreCase("*")) return false; 2278 //search variable in framestack 2279 2280 TVariable symbolVariable = null; 2281 2282 if (cr.getTableToken() != null){ 2283 // record_variable.column 2284 symbolVariable = TSymbolTableManager.searchSymbolVariable(this.getFrameStack(),cr.getTableToken().toString()); 2285 if (symbolVariable != null){ 2286 cr.getTableToken().setDbObjectType(EDbObjectType.variable); 2287 //TTable sourceTable = new TTable(new TObjectName(EDbObjectType.table,symbolVariable.getVariableName().getStartToken())); 2288 TTable sourceTable = new TTable(TObjectName.createObjectName (this.dbvendor, EDbObjectType.variable,symbolVariable.getVariableName().getStartToken())); 2289 sourceTable.getLinkedColumns().addObjectName(cr); 2290 cr.setSourceTable(sourceTable); 2291 // symbolVariable.getVariableName().getReferencedObjects().addObjectName(cr); 2292 // System.out.println("find variable:"+cr.toString()); 2293 cr.setResolveStatus(TBaseType.RESOLVED_AND_FOUND); // set resolve status to resolved,避免在 TAttributeResolver 中关联到其他 table 2294 return true; 2295 } 2296 2297 }else{ 2298 // variable 2299 symbolVariable = TSymbolTableManager.searchSymbolVariable(this.getFrameStack(),cr.toString()); 2300 if (symbolVariable != null){ 2301 cr.setDbObjectType(EDbObjectType.variable); 2302 symbolVariable.getVariableName().getReferencedObjects().addObjectName(cr); 2303 return true; 2304 } 2305 } 2306 2307 // check parameters in plsql only, may add support for sql server later. 2308 if(! ((dbvendor == EDbVendor.dbvoracle)||(dbvendor == EDbVendor.dbvmysql))) return false; 2309 if (cr.getObjectType() == TObjectName.ttobjVariable) return true; 2310 2311 Stack symbolTable = this.getTopStatement().getSymbolTable(); 2312 TSymbolTableItem item = null; 2313 TObjectName objName = null; 2314 TObjectName qualifiedName = null; // function/procedure name or label name of plsql block 2315 for (int i = symbolTable.size()-1;i>=0;i--){ 2316 item = (TSymbolTableItem)symbolTable.get(i); 2317 if (item.getData() instanceof TParameterDeclaration){ 2318 objName = ((TParameterDeclaration)item.getData()).getParameterName(); 2319 }else if (item.getData() instanceof TVarDeclStmt){ 2320 objName = ((TVarDeclStmt)item.getData()).getElementName(); 2321 }else if (item.getData() instanceof TObjectName){ 2322 objName = (TObjectName)item.getData(); 2323 } 2324 2325 if (objName == null) continue; 2326 2327 // strip quote characters so a quoted declaration ("P_DATE" IN NUMBER) 2328 // still matches an unquoted reference P_DATE (MantisBT 4504) 2329 if (TBaseType.getTextWithoutQuoted(cr.toString()) 2330 .compareToIgnoreCase(TBaseType.getTextWithoutQuoted(objName.toString())) == 0){ 2331 ret = true; 2332 if (checkVariableDeclaredInProcedure) break; // return true if variable declared in procedure 2333 for(int j=0;i<tables.size();i++){ 2334 TTable lcTable = tables.getTable(j); 2335 if (lcTable.isBaseTable()){ 2336 if (fireOnMetaDatabaseTableColumn( 2337 lcTable.getPrefixServer() 2338 ,lcTable.getPrefixDatabase() 2339 ,lcTable.getPrefixSchema() 2340 ,lcTable.getName() 2341 ,cr.getColumnNameOnly())){ 2342 ret = false; 2343 break; 2344 } 2345 } 2346 } 2347 2348 if (ret) break; 2349 }else if (cr.toString().indexOf(".")>0){ 2350 // qualified object reference, compare it with procedure/function/block label prefixed 2351 if (item.getStmt() instanceof TPlsqlCreateFunction){ 2352 qualifiedName = ((TPlsqlCreateFunction)item.getStmt()).getFunctionName(); 2353 }else if (item.getStmt() instanceof TPlsqlCreateProcedure){ 2354 qualifiedName = ((TPlsqlCreateProcedure)item.getStmt()).getProcedureName(); 2355 }else if (item.getStmt() instanceof TCommonBlock){ 2356 qualifiedName = ((TCommonBlock)item.getStmt()).getLabelName(); 2357 } 2358 2359 if (qualifiedName != null){ 2360 if (TBaseType.getTextWithoutQuoted(cr.toString()).compareToIgnoreCase( 2361 TBaseType.getTextWithoutQuoted(qualifiedName.toString()) + '.' 2362 + TBaseType.getTextWithoutQuoted(objName.toString())) == 0){ 2363 ret = true; 2364 } 2365 } 2366 2367 if (ret ) break; 2368 } 2369 2370 } 2371 if (ret){ 2372 //add this parameter or variable reference to original parameter/variable 2373 objName.getReferencedObjects().addObjectName(cr); 2374 cr.setObjectType(TObjectName.ttobjVariable); 2375 } 2376 return ret; 2377 } 2378 2379 TCTE findCTEByName(String cteName){ 2380 TCTEList lcCteList = searchCTEList(false); 2381 TCTE lcCte = null; 2382 if (lcCteList != null){ 2383 for(int i=0;i<lcCteList.size();i++){ 2384 if (lcCteList.getCTE(i).getTableName().toString().compareToIgnoreCase(cteName)==0){ 2385 lcCte = lcCteList.getCTE(i); 2386 break; 2387 } 2388 } 2389 } 2390 return lcCte; 2391 } 2392 2393 /** 2394 * @deprecated since 2.3.8.2, use {@link TTable#getExpandedStarColumns()} instead. 2395 * 2396 * @param lcTable 2397 * @return 2398 */ 2399 public ArrayList<String> getColumnsInTable(TTable lcTable){ 2400 if (lcTable.isCTEName()){ 2401 ArrayList<String> columns = new ArrayList<>(); 2402 if (lcTable.getCteColomnReferences()!=null){ 2403 for(TObjectName n:lcTable.getCteColomnReferences()){ 2404 columns.add(n.toString()); 2405 } 2406 }else if (lcTable.getCTE().getSubquery() != null && lcTable.getCTE().getSubquery().getResultColumnList() != null){ 2407 for(TResultColumn resultColumn:lcTable.getCTE().getSubquery().getResultColumnList()){ 2408 columns.add(resultColumn.getDisplayName()); 2409 } 2410 } 2411 return columns; 2412 }else{ 2413 return getColumnsInTable( 2414 lcTable.getPrefixServer() 2415 ,lcTable.getPrefixDatabase() 2416 ,lcTable.getPrefixSchema() 2417 ,lcTable.getName() 2418 ); 2419 } 2420 } 2421 2422 2423 /** 2424 * @deprecated since 2.3.8.2, use {@link TTable#getExpandedStarColumns()} instead. 2425 * 2426 * @param pServer 2427 * @param pDatabase 2428 * @param pSchema 2429 * @param pTable 2430 * @return 2431 */ 2432 public ArrayList<String> getColumnsInTable(String pServer,String pDatabase,String pSchema,String pTable){ 2433 TFrame stackFrame = getFrameStack().firstElement(); 2434 TGlobalScope globalScope = (TGlobalScope)stackFrame.getScope(); 2435 2436 if (globalScope.getSqlEnv() != null){ 2437 return globalScope.getSqlEnv().getColumnsInTable(pDatabase+"."+pSchema+"."+pTable,false); 2438 }else{ 2439 return null; 2440 } 2441 } 2442 2443 2444 public boolean fireOnMetaDatabaseTableColumn(String pServer,String pDatabase,String pSchema,String pTable,String pColumn){ 2445// boolean lcResult = false; 2446// if (this.getGsqlparser().getMetaDatabase() != null){ 2447// lcResult = this.getGsqlparser().getMetaDatabase().checkColumn(pServer,pDatabase,pSchema,pTable,pColumn); 2448// } 2449 2450 TFrame stackFrame = getFrameStack().firstElement(); 2451 TGlobalScope globalScope = (TGlobalScope)stackFrame.getScope(); 2452 2453 if (globalScope.getSqlEnv() != null){ 2454 // System.out.println(globalScope.getSqlEnv().toString()); 2455 2456 return globalScope.getSqlEnv().columnInTable(pDatabase+"."+pSchema+"."+pTable,pColumn); 2457 }else{ 2458 return false; 2459 } 2460 2461// return lcResult; 2462 } 2463 2464 public TTable getFirstPhysicalTable(){ 2465 TTable ret = null; 2466 if (tables.size() == 0) return null; 2467 for(int i=0;i<tables.size();i++){ 2468 if (tables.getTable(i).isBaseTable()) { 2469 ret = tables.getTable(i); 2470 break; 2471 } 2472 } 2473 return ret; 2474 } 2475 private TObjectNameList orphanColumns = null; 2476 2477 public TObjectNameList getOrphanColumns() { 2478 if (orphanColumns == null) orphanColumns = new TObjectNameList(); 2479 return orphanColumns; 2480 } 2481 2482 protected boolean linkToFirstTable(TObjectName pColumn,int pCandidateTableCnt){ 2483 boolean lcResult = false; 2484 // ClickHouse expression-CTE alias resolved by an outer scope during 2485 // the parent-walk: the reference is a scalar symbol — guessing it onto 2486 // this scope's first table would fabricate lineage (and did, when 2487 // metadata ruled the inner table out and the outer WITH alias won). 2488 if ((pColumn.getExpressionCteRef() != null) && (pColumn.getSourceTable() == null)){ 2489 return true; 2490 } 2491 if ((dbvendor == EDbVendor.dbvteradata)&&(pColumn.isQualified())&&(pColumn.getTableToken().getDbObjectType() != EDbObjectType.subquery_alias)){ 2492 // update table1 set col = 'value' where table1.id = table2.id2 2493 boolean isFoundLinkedTable = false; 2494 TCustomSqlStatement lcSql = this; 2495 while (lcSql != null){ 2496 int i = 0; 2497 i = lcSql.tables.searchTableByNameOrAlias(pColumn.getTableToken().toString()); 2498 isFoundLinkedTable = ( i != -1); 2499 if (isFoundLinkedTable) { 2500 if (lcSql.tables.getTable(i).getEffectType() == ETableEffectType.tetImplicitLateralDerivedTable ){ 2501 // 如果不查重table,会导致 employee.first_name 中的 employee 被第二次加到 tables 中 2502// DELETE FROM foodmart.trimmed_employee ACT 2503// WHERE ACT.employee_id = employee.employee_id 2504// AND employee.first_name = 'Walter' 2505// AND trimmed_salary.employee_id = -1 2506 2507 TTable newTable = lcSql.tables.getTable(i); 2508 newTable.getLinkedColumns().addObjectName(pColumn); 2509 pColumn.setSourceTable(newTable); 2510 pColumn.setValidate_column_status(TBaseType.COLUMN_LINKED_TO_TABLE_IN_OLD_ALGORITHM); 2511 } 2512 break; 2513 } 2514 lcSql = lcSql.getParentStmt(); 2515 } 2516 if (!isFoundLinkedTable){ 2517 TTable newTable = null; 2518 if (pColumn.getDatabaseToken() == null){ 2519 2520 //newTable = new TTable(new TObjectName(EDbObjectType.table,pColumn.getTableToken())); 2521 newTable = new TTable(TObjectName.createObjectName (this.dbvendor, EDbObjectType.table,pColumn.getTableToken())); 2522 newTable.setStartToken(pColumn.getTableToken()); 2523 newTable.setEndToken(pColumn.getTableToken()); 2524 }else{ 2525 2526 //newTable = new TTable(new TObjectName(EDbObjectType.table,pColumn.getSchemaToken(), pColumn.getTableToken())); 2527 newTable = new TTable(TObjectName.createObjectName (this.dbvendor,EDbObjectType.table,pColumn.getDatabaseToken(), pColumn.getTableToken())); 2528 newTable.setStartToken(pColumn.getSchemaToken()); 2529 newTable.setEndToken(pColumn.getTableToken()); 2530 } 2531 2532 newTable.setTableType(ETableSource.objectname); 2533 newTable.setEffectType(ETableEffectType.tetImplicitLateralDerivedTable); 2534 newTable.getLinkedColumns().addObjectName(pColumn); 2535 pColumn.setSourceTable(newTable); 2536 pColumn.setValidate_column_status(TBaseType.COLUMN_LINKED_TO_TABLE_IN_OLD_ALGORITHM); 2537 this.addToTables(newTable); 2538 2539 // 2024 年 2540 // 不能加入到 relations 中,否则会导致 下面 SQL 中 star column 同时链接到 SPCOMM.L_FIXED_RATE_PLAN_REF, ipshare_ofccplv.cprof_d_period_dates_ref 2541 // 从而导致 本来不该有的歧义产生 2542 2543 // UPDATE b_rate_plan 2544 //FROM 2545 //( 2546 //SELECT * FROM SPCOMM.L_FIXED_RATE_PLAN_REF 2547 //WHERE rate_plan_ref_eff_dt<= ipshare_ofccplv.cprof_d_period_dates_ref.PERIOD 2548 //) AS ref 2549 //SET accs_fee = REF.accs_fee, 2550 //SVC_TYPE = REF.prod_grp_lvl_1, 2551 //rate_plan_lvl3 = REF.rate_plan_lvl_3, 2552 //prod_grp_lvl3 = REF.prod_grp_lvl_2 2553 //WHERE b_rate_plan.svc_type IS NULL 2554 2555 // 2025/2/25, v3.0.4.8 2556 // 需要加入到 relations 中,新的 gudusoft.gsqlparser.resolver package 中的算法会处理这种情况 2557 // teradata 的隐式横向派生表不能加入到关系解析器中 2558 // this.getRelations().add(newTable); 2559 } 2560 return true; 2561 } 2562 if (pColumn.getCandidateTables().size() == 1){ 2563 TTable table = pColumn.getCandidateTables().getTable(0); 2564 table.getLinkedColumns().addObjectName(pColumn); 2565 pColumn.setSourceTable(table); 2566 lcResult = true; 2567 } 2568 else if ((tables.size() == 1) || (pCandidateTableCnt == 1)){ 2569 TTable table = tables.getTable(0); 2570 2571 if(table.getTableType() == ETableSource.function){ 2572 //lcResult = linkToFunctionTable(table, pColumn); 2573 int iRet = table.getFuncCall().isColumnInThisTableFunction(this.getSqlEnv(),this.dbvendor,pColumn); 2574 if ( iRet == TBaseType.COLUMN_IN_TABEL_FUNCTION_YES){ 2575 lcResult = true; 2576 table.getLinkedColumns().addObjectName(pColumn); 2577 pColumn.setSourceTable(table); 2578 lcResult = true; 2579 }else if ( iRet == TBaseType.COLUMN_IN_TABEL_FUNCTION_NO){ 2580 lcResult = false; 2581 }else{ 2582 table.getLinkedColumns().addObjectName(pColumn); 2583 pColumn.setSourceTable(table); 2584 lcResult = true; 2585 } 2586 }else if(table.getTableType() == ETableSource.subquery){ 2587 if (! table.getSubquery().searchColumnInResultSet(pColumn,(tables.size()==1))){ 2588 getOrphanColumns().addObjectName(pColumn); 2589 pColumn.setOrphanColumn(true); 2590 pColumn.setOwnStmt(this); 2591 TSourceToken st = pColumn.getStartToken(); 2592 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){ 2593 TBaseType.log(String.format("Add orphan column <%s> to statement in old algorithm in subquery %s",pColumn.toString(),table.getAliasName()),TLog.WARNING,table); 2594 } 2595 this.parseerrormessagehandle(new TSyntaxError(st.getAstext(), st.lineNo, st.columnNo 2596 ,"find orphan column", EErrorType.sphint 2597 , TBaseType.MSG_HINT_FIND_ORPHAN_COLUMN,this,st.posinlist,pColumn)); 2598 } 2599 } 2600 else{ 2601 table.getLinkedColumns().addObjectName(pColumn); 2602 pColumn.setSourceTable(table); 2603 lcResult = true; 2604 if ((dbvendor == EDbVendor.dbvbigquery)&&(pCandidateTableCnt == 0) && (pColumn.isQualified())){ 2605 // bigquery struct column used in query 2606// create view test as (SELECT rollNo, 2607// info.name as n1, 2608// info2.name as n2, 2609// info.age from my_first_dataset.student_records); 2610 2611 pColumn.columnToProperty(); 2612 } 2613 } 2614 }else if (tables.size() > 1){ 2615 // if there is only a table without table alias, then, link to this table 2616 boolean foundOnlyOneTable = false; 2617 TTable tableWithoutAlias = null; 2618 for(TTable table:tables){ 2619 if (table.isCTEName()) continue; // CTE 即便没有 指定alias,也不作为考虑对象 2620 if (table.getAliasClause() == null){ 2621 tableWithoutAlias = table; 2622 if (foundOnlyOneTable){ 2623 foundOnlyOneTable = false; 2624 break; 2625 }else{ 2626 foundOnlyOneTable = true; 2627 } 2628 } 2629 } 2630 2631 if (foundOnlyOneTable){ 2632 tableWithoutAlias.getLinkedColumns().addObjectName(pColumn); 2633 pColumn.setSourceTable(tableWithoutAlias); 2634 lcResult = true; 2635 }else{ 2636 getOrphanColumns().addObjectName(pColumn); 2637 pColumn.setOrphanColumn(true); 2638 pColumn.setOwnStmt(this); 2639 TSourceToken st = pColumn.getStartToken(); 2640 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){ 2641 TBaseType.log(String.format("Add orphan column <%s> to statement in old algorithm ",pColumn.toString()),TLog.WARNING,this); 2642 } 2643 2644 this.parseerrormessagehandle(new TSyntaxError(st.getAstext(), st.lineNo, st.columnNo 2645 ,"find orphan column", EErrorType.sphint 2646 , TBaseType.MSG_HINT_FIND_ORPHAN_COLUMN,this,st.posinlist,pColumn)); 2647 } 2648 2649 } 2650 return lcResult; 2651 } 2652 2653 private boolean linkToFunctionTable(TTable table, TObjectName pColumn) { 2654 if(table.getTableName().toString().toUpperCase().equals("STRING_SPLIT")){ 2655 if(pColumn.getColumnNameOnly().toUpperCase().equals("VALUE")){ 2656 table.getLinkedColumns().addObjectName(pColumn); 2657 pColumn.setSourceTable(table); 2658 return true; 2659 } 2660 else return false; 2661 } 2662 else { 2663 table.getLinkedColumns().addObjectName(pColumn); 2664 pColumn.setSourceTable(table); 2665 return true; 2666 } 2667 } 2668 2669 2670 private TTable findInsertedOrDeleteTable(TTable table) { 2671 if (table == null) return null; 2672 2673 // Check if this table is 'inserted' by examining both name and toString() 2674 // The inserted table might have different representations 2675 String tableName = table.getName(); 2676 String tableString = table.toString(); 2677 2678 if ("inserted".equalsIgnoreCase(tableName) || 2679 "inserted".equalsIgnoreCase(tableString) || 2680 "deleted".equalsIgnoreCase(tableName) || 2681 "deleted".equalsIgnoreCase(tableString)) { 2682 return table; 2683 } 2684 2685 // If this is a join table, recursively check its components 2686 if (table.getTableType() == ETableSource.join && table.getJoinExpr() != null) { 2687 TJoinExpr joinExpr = table.getJoinExpr(); 2688 2689 // Check left side recursively 2690 TTable leftResult = findInsertedOrDeleteTable(joinExpr.getLeftTable()); 2691 if (leftResult != null) { 2692 return leftResult; 2693 } 2694 2695 // Check right side recursively 2696 TTable rightResult = findInsertedOrDeleteTable(joinExpr.getRightTable()); 2697 if (rightResult != null) { 2698 return rightResult; 2699 } 2700 } 2701 2702 return null; 2703 } 2704 2705 2706 boolean isSQLServerInsertedDelete(TObjectName pColumn){ 2707 if (dbvendor != EDbVendor.dbvmssql) return false; 2708 2709 // only process sql server inserted delete column, if not then return false 2710 // if (!((pColumn.toString().toUpperCase().startsWith("INSERTED"))||(pColumn.toString().toUpperCase().startsWith("DELETED")))) return false; 2711 if (!((pColumn.getStartToken().tokencode == TBaseType.rrw_sqlserver_INSERTED ) 2712 ||(pColumn.getStartToken().tokencode == TBaseType.rrw_sqlserver_DELETED ))) return false; 2713 2714 // we need to get the target table in this statement's from clause which can be complex join, so we need to iterate all tables in this statement's from clause 2715 // to find the target table in the literal as 'inserted' or 'deleted'. 2716 2717 TTable lcTargetTable = null; 2718 for(TTable table : this.getRelations()){ 2719 lcTargetTable = findInsertedOrDeleteTable(table); 2720 if (lcTargetTable != null){ 2721 break; 2722 } 2723 } 2724 2725 if (lcTargetTable == null) return false; 2726 2727 2728 if (lcTargetTable.getLinkTable() != null){ 2729 lcTargetTable.getLinkTable().getLinkedColumns().addObjectName(pColumn); 2730 pColumn.setSourceTable(lcTargetTable.getLinkTable()); 2731 2732 pColumn.setResolveStatus(TBaseType.RESOLVED_AND_FOUND); // 避免在 // TAttributeResolver 中再次进行处理,关联到其他 table 2733 2734 }else{ 2735 lcTargetTable.getLinkedColumns().addObjectName(pColumn); 2736 pColumn.setSourceTable(lcTargetTable); 2737 pColumn.setResolveStatus(TBaseType.RESOLVED_AND_FOUND); // 避免在 // TAttributeResolver 中再次进行处理,关联到其他 table 2738 } 2739 return true; 2740 2741 } 2742 2743 boolean isOracleNewOldTable(TObjectName pColumn){ 2744 boolean ret = false; 2745 if (dbvendor != EDbVendor.dbvoracle) return false; 2746 if (!(pColumn.isQualified())) return false; 2747 if ((pColumn.getTableString().equalsIgnoreCase(":new")) 2748 ||(pColumn.getTableString().equalsIgnoreCase(":old"))){ 2749 if (getAncestorStmt().tables != null){ 2750 if (getAncestorStmt().tables.size() > 0){ 2751 getAncestorStmt().tables.getTable(0).getLinkedColumns().addObjectName(pColumn); 2752 pColumn.setSourceTable(getAncestorStmt().tables.getTable(0)); 2753 ret = true; 2754 } 2755 } 2756 } 2757 2758 return ret; 2759 } 2760 2761 public boolean searchDaxVariableInStack(TObjectName pName){ 2762 boolean ret = false; 2763 if (getVariableStack().size() == 0) return false; 2764 if (pName.getDbObjectType() == EDbObjectType.column) return false; 2765 for(int i=0;i<variableStack.size();i++){ 2766 if (pName.toString().equalsIgnoreCase(((TObjectName) variableStack.get(i)).toString())){ 2767 ret = true; 2768 break; 2769 } 2770 } 2771 return ret; 2772 } 2773 2774 boolean linkColumnToTableDax(TObjectName pColumn, ESqlClause pLocation){ 2775 boolean lcResult = true ; 2776 TDaxFunction daxFunction = null; 2777 if (searchDaxVariableInStack(pColumn)) return false; 2778 if (getDaxFunctionStack().size() > 0){ 2779 daxFunction = daxFunctionStack.peek(); 2780 } 2781 2782 if (pColumn.getTableToken() != null){ 2783 //TTable sourceTable = new TTable(new TObjectName(EDbObjectType.table,pColumn.getTableToken())); 2784 TTable sourceTable = new TTable(TObjectName.createObjectName (this.dbvendor,EDbObjectType.table,pColumn.getTableToken())); 2785 sourceTable.getLinkedColumns().addObjectName(pColumn); 2786 addToTables(sourceTable); 2787 if ((daxFunction != null) && (daxFunction.getDefaultTable() == null)){ 2788 daxFunction.setDefaultTable(sourceTable); 2789 } 2790 }else{ 2791 if ((daxFunction != null) &&(daxFunction.getDefaultTable() != null)){ 2792 daxFunction.getDefaultTable().getLinkedColumns().addObjectName(pColumn); 2793 }else{ 2794 ((TDaxStmt)this).getDefaultTable().getLinkedColumns().addObjectName(pColumn); 2795 } 2796 } 2797 return lcResult; 2798 } 2799 2800 /** 2801 * 将列引用解析并绑定到其来源(表、子查询、CTE、表函数、OPENQUERY/UNNEST 等)。 2802 * 2803 * 功能概述: 2804 * 1) 针对 DAX 语法直接走 DAX 分支。 2805 * 2) 已绑定或标记“延迟到列解析器”的列直接返回。 2806 * 3) 设定列所在语法位置,并校验列名/保留字(含 MySQL true/false/default、内置函数等)。 2807 * 4) 处理厂商伪表/特殊前缀(Oracle :new/:old;SQL Server INSERTED/DELETED)。 2808 * 5) Insert All/VALUES 场景:优先在子查询结果集中/变量或过程参数中匹配。 2809 * 6) 在当前语句的 FROM 表集合中查找并建立绑定: 2810 * - 限定列 table.col:按别名/表名匹配;对子查询/CTE/OPENQUERY 进一步在结果集中定位源列; 2811 * 命中后写入 linkedColumns,必要时将 TableToken 标记为 subquery_alias。 2812 * - 非限定列 col: 2813 * a. 先尝试同层 SELECT 列别名(支持 LATERAL 语义且位置在别名之后); 2814 * b. 处理通配符“*”:收集所有来源表; 2815 * c. 基础表通过元数据回调 fireOnMetaDatabaseTableColumn 校验;未命中则记录候选; 2816 * d. 子查询/CTE/函数/UNNEST/PIVOT 分别按各自规则匹配。 2817 * 7) 命中后将列加入表的 linkedColumns 并设置 sourceTable/sourceColumn,必要时维持 isContinue 以继续匹配“*”。 2818 * 8) 若未命中:尝试变量/参数;再按条件(语句类型/位置/是否限定等)向上一层语句递归查找(维护 searchLevel)。 2819 * 9) 仍未命中:在顶层(searchLevel==0)按“候选唯一/或首表”兜底策略 {@link #linkToFirstTable(TObjectName, int)}。 2820 * 2821 * 参数: 2822 * @param pColumn 需要绑定的列名对象(方法会更新其 location、sourceTable、sourceColumn 等) 2823 * @param pLocation 列出现的语法位置(如 selectList、where、insertValues 等) 2824 * 2825 * 返回值: 2826 * @return 成功绑定到某个来源返回 true;未能绑定或被识别为变量/保留字等返回 false 2827 * 2828 * 厂商兼容: 2829 * - Oracle: 处理 :new/:old,Insert All 的 values 子句源自子查询的匹配 2830 * - SQL Server: 处理 INSERTED/DELETED 伪表 2831 * - MySQL: 对保留字/布尔字面量/内置函数名的特殊判断 2832 * - DAX: 委托 {@link #linkColumnToTableDax(TObjectName, ESqlClause)} 2833 * 2834 * 副作用: 2835 * - 更新 pColumn 的 location/searchLevel/sourceTable/sourceColumn/validate 状态 2836 * - 向命中的表写入 linkedColumns 或向别名列写入 targetColumns 2837 * - 对“*”列填充 sourceTableList;对子查询命中时可能将 TableToken 标为 subquery_alias 2838 * - 记录候选表数量并填充 pColumn.candidateTables,用于后续兜底 2839 * 2840 * 复杂度与顺序: 2841 * - 优先使用同层信息(别名/元数据/子查询结果),再逐层向外查找;避免无谓的上层搜索 2842 * 2843 * 注意: 2844 * - 本方法完成“旧算法”的快速联接,新的解析/消歧逻辑在解析器(如 TStmtScope/TAttributeResolver)中继续处理 2845 */ 2846 /** 2847 * ClickHouse only: the expression-CTE ({@code WITH <expr> AS ident}) whose 2848 * alias matches the unqualified column reference, or null. The search 2849 * covers this statement's own WITH list and ascends ONLY through 2850 * set-operation membership (a compound query's WITH list is shared with 2851 * its members). It must NOT walk arbitrary lexical parents: ClickHouse 2852 * resolves closest-scope-first, so an outer alias may only win after this 2853 * scope's own tables failed — which the existing parent-walk recursion in 2854 * linkColumnToTable already provides (the recursion re-enters this check 2855 * at the parent's own level). A reference located inside a CTE's own 2856 * defining expression never matches that CTE (it is a column named like 2857 * the alias, not a self-reference). 2858 */ 2859 private TCTE findExpressionCteInList(TCTEList lcCteList, TObjectName pColumn){ 2860 if (lcCteList == null) return null; 2861 for(int i=0;i<lcCteList.size();i++){ 2862 TCTE lcCte = lcCteList.getCTE(i); 2863 if ((lcCte.getExpression() == null) || (lcCte.getTableName() == null)) continue; 2864 if ((pColumn.getStartToken() != null) 2865 && (lcCte.getExpression().getStartToken() != null) 2866 && (lcCte.getExpression().getEndToken() != null) 2867 && (pColumn.getStartToken().posinlist >= lcCte.getExpression().getStartToken().posinlist) 2868 && (pColumn.getStartToken().posinlist <= lcCte.getExpression().getEndToken().posinlist)){ 2869 continue; 2870 } 2871 if (SQLUtil.sameName(dbvendor, ESQLDataObjectType.dotColumn, 2872 pColumn.toString(), lcCte.getTableName().toString())){ 2873 return lcCte; 2874 } 2875 } 2876 return null; 2877 } 2878 2879 private TCTE findExpressionCteForAlias(TObjectName pColumn){ 2880 TCustomSqlStatement stmt = this; 2881 while (stmt != null){ 2882 TCTE found = findExpressionCteInList(stmt.getCteList(), pColumn); 2883 if (found != null) return found; 2884 if (!((stmt instanceof TSelectSqlStatement) 2885 && ((TSelectSqlStatement)stmt).isChildOfCombinedQuery())) break; 2886 stmt = stmt.parentStmt; 2887 } 2888 return null; 2889 } 2890 2891 /** 2892 * ClickHouse only: like {@link #findExpressionCteForAlias} but across every 2893 * enclosing statement. Used ONLY where the local scope has already failed 2894 * to PROVE the column (e.g. an unverifiable table-function column) — a 2895 * proven outer alias then outranks the local guess. 2896 */ 2897 private TCTE findExpressionCteForAliasInAnyScope(TObjectName pColumn){ 2898 for(TCustomSqlStatement stmt = this; stmt != null; stmt = stmt.parentStmt){ 2899 TCTE found = findExpressionCteInList(stmt.getCteList(), pColumn); 2900 if (found != null) return found; 2901 } 2902 return null; 2903 } 2904 2905 public boolean linkColumnToTable(TObjectName pColumn, ESqlClause pLocation){ 2906 boolean lcResult = false,isContinue = false; 2907 int candidateTableCnt = 0; 2908 if (pColumn == null) return false; 2909 if (dbvendor == EDbVendor.dbvdax){ 2910 return linkColumnToTableDax(pColumn,pLocation); 2911 } 2912 2913 // Skip alias definition columns - they define column names in alias clauses, not column references 2914 // Example: In "AS x (numbers, animals)", numbers and animals are column_alias type 2915 if (pColumn.getDbObjectType() == EDbObjectType.column_alias) { 2916 return true; 2917 } 2918 2919 if (pColumn.getSourceTable() != null) { 2920 lcResult = true; 2921 return lcResult; 2922 } 2923 2924 if (pColumn.getResolveStatus() == TBaseType.RESOLVE_DELAY_TO_COLUMN_RESOLVER) return true; 2925 2926 pColumn.setLocation(pLocation); 2927 2928 if (! pColumn.isValidColumnName(dbvendor)) { 2929 if (pColumn.isReservedKeyword()){ 2930 if ( 2931 ((pColumn.getStartToken().tokencode != TBaseType.rrw_mysql_true)&&(!(pColumn.getStartToken().toString().equalsIgnoreCase("true")))) 2932 &&((pColumn.getStartToken().tokencode != TBaseType.rrw_mysql_false)&&(!(pColumn.getStartToken().toString().equalsIgnoreCase("false")))) 2933 &&(pColumn.getStartToken().tokencode != TBaseType.rrw_mysql_default) 2934 &&(pColumn.getStartToken().tokencode != TBaseType.rrw_on)&&(!(pColumn.getStartToken().toString().equalsIgnoreCase("on"))) 2935 ) { 2936 boolean mysqlBuiltFunction = false; 2937 if (dbvendor == EDbVendor.dbvmysql){ 2938 mysqlBuiltFunction = functionChecker.isBuiltInFunction(pColumn.toString(),EDbVendor.dbvmysql,"6.0"); 2939 } 2940 if (!mysqlBuiltFunction){ 2941 TSourceToken st1 = pColumn.getStartToken(); 2942 TSyntaxError err = new TSyntaxError(st1.toString() 2943 , st1.lineNo, st1.columnNo 2944 , String.format("Reserved keyword can't be column name") 2945 , EErrorType.sperror 2946 , TBaseType.MSG_ERROR_RESERVED_KEYWORD_CANT_USED_AS_COLUMN_NAME 2947 ,this,st1.posinlist); 2948 this.parseerrormessagehandle(err); 2949 } 2950 } 2951 } 2952 return false; 2953 } 2954 2955 if (isOracleNewOldTable(pColumn)) return true; 2956 if (isSQLServerInsertedDelete(pColumn)) return true; 2957 2958 // oracle insert all statement, 2959 // WHEN id <= 3 THEN INTO dest_tab1 VALUES(id, description1) 2960 // column in values clause must be in the subquery of insert all statement 2961 if (pColumn.getLocation() == ESqlClause.insertValues){ 2962 if (this instanceof TInsertSqlStatement){ 2963 TInsertSqlStatement insertSqlStatement = (TInsertSqlStatement)this; 2964 if (insertSqlStatement.isInsertAll()){ 2965 // if (pColumn.getStartToken().tokencode == TBaseType.rrw_snowflake_default) return true; 2966 lcResult = insertSqlStatement.getSubQuery().searchColumnInResultSet(pColumn, true); 2967 } 2968 } 2969 2970 if (lcResult) return true; 2971 2972 // value in values clause maybe parameter of the procedure/function parameter 2973 lcResult = locateVariableOrParameter(pColumn,true); 2974 if (lcResult) return true; 2975 } 2976 2977 // ClickHouse expression-CTE alias: WITH <expr> AS ident makes ident a 2978 // scalar symbol of the consuming query, not a column of its FROM 2979 // tables (default ClickHouse semantics: the alias shadows a 2980 // same-named table column). Binding it to a table would fabricate 2981 // lineage; resolve it to the CTE instead, before any table search. 2982 if ((dbvendor == EDbVendor.dbvclickhouse) && (!pColumn.isQualified())){ 2983 TCTE lcExprCte = findExpressionCteForAlias(pColumn); 2984 if (lcExprCte != null){ 2985 pColumn.setExpressionCteRef(lcExprCte); 2986 pColumn.setResolveStatus(TBaseType.RESOLVED_AND_FOUND); 2987 return false; 2988 } 2989 } 2990 2991 boolean foundInMetaData = false; 2992 2993 for(int i=0;i<tables.size();i++){ 2994 TTable lcTable = tables.getTable(i); 2995 if (lcTable.getEffectType() == ETableEffectType.tetSelectInto) continue; 2996 if (lcTable.getEffectType() == ETableEffectType.tetImplicitLateralDerivedTable) continue; 2997 2998 if (pColumn.isQualified()){ 2999 lcResult = pColumn.resolveWithThisTable(lcTable); 3000 if ((lcResult) && (lcTable.getTableType() == ETableSource.subquery)){ 3001 pColumn.getTableToken().setDbObjectType(EDbObjectType.subquery_alias); 3002 3003 int lcPos = lcTable.searchColumnInAlias(pColumn); 3004 lcResult = lcPos>=0; 3005 if (lcResult){ 3006 // 在 alias 中找到 source column, 还需要对应到 subquery select 中的 select list 3007 // sql 见 https://e.gitee.com/gudusoft/projects/151613/tasks/list?issue=I8JR0W#note_23051633 3008 if ((lcTable.getSubquery() != null)&&(lcTable.getSubquery().getResultColumnList() != null)){ 3009 pColumn.setSourceColumn(lcTable.getSubquery().getResultColumnList().getResultColumn(lcPos)); 3010 } 3011 }else{ 3012 lcResult = lcTable.getSubquery().searchColumnInResultSet(pColumn,true); 3013 } 3014 // lcTable.getSubquery().searchColumnInResultSet(pColumn,true); 3015 3016 }else if ((lcResult) && (lcTable.isCTEName())){ 3017 lcTable.getCTE().searchColumnInResultSet(this,lcTable,pColumn,true); 3018 }else if ((lcResult) && (lcTable.getTableType() == ETableSource.openquery)&&(lcTable.getSubquery() != null)){ 3019 pColumn.getTableToken().setDbObjectType(EDbObjectType.subquery_alias); 3020 lcTable.getSubquery().searchColumnInResultSet(pColumn,true); 3021// }else if ((lcResult) && (lcTable.getTableType() == ETableSource.unnest)&&(lcTable.getUnnestClause() != null)){ 3022// pColumn.getTableToken().setDbObjectType(EDbObjectType.subquery_alias); 3023// lcTable.getSubquery().searchColumnInResultSet(pColumn,true); 3024 } 3025 if (lcResult&&pColumn.toString().endsWith("*")){ 3026 pColumn.getSourceTableList().add(lcTable); 3027// ArrayList<String> lcColumns = getColumnsInTable(lcTable); 3028// if (lcColumns != null){ 3029// pColumn.getColumnsLinkedToStarColumn().addAll(lcColumns); 3030// } 3031 } 3032 }else { 3033 // column not qualified 3034 3035 // check if this is the column alias in current select list. 3036 // The binding rule itself lives in TResultColumnList.findLateralAliasDefinition() 3037 // so that this path and resolver2's handleSelectListAliasResolution() can never 3038 // disagree about what counts as an alias reference (MantisBT 4659). 3039 if((!lcResult)&& ((!pColumn.isQualified()) && (this instanceof TSelectSqlStatement)&&(getResultColumnList() !=null))){ 3040 TResultColumn lcField = getResultColumnList().findLateralAliasDefinition(dbvendor, pColumn); 3041 if (lcField != null){ 3042 pColumn.setSourceColumn(lcField); 3043 lcField.getTargetColumns().addObjectName(pColumn); 3044 pColumn.setValidate_column_status(TBaseType.COLUMN_LINKED_TO_COLUMN_ALIAS_IN_OLD_ALGORITHM); 3045 return true; 3046 } 3047 } 3048 3049 if (pColumn.getColumnNameOnly().equalsIgnoreCase("*")){ 3050 lcResult = true; 3051 isContinue = true; // in order to match next table in the from clause 3052 pColumn.getSourceTableList().add(lcTable); 3053// ArrayList<String> lcColumns = getColumnsInTable(lcTable); 3054// if (lcColumns != null){ 3055// pColumn.getColumnsLinkedToStarColumn().addAll(lcColumns); 3056// } 3057 }else if (lcTable.isBaseTable()){ 3058 lcResult = fireOnMetaDatabaseTableColumn( 3059 lcTable.getPrefixServer() 3060 ,lcTable.getPrefixDatabase() 3061 ,lcTable.getPrefixSchema() 3062 ,lcTable.getName() 3063 ,pColumn.getColumnNameOnly()); 3064 if (! lcResult) { 3065 candidateTableCnt++; 3066 pColumn.getCandidateTables().addTable(lcTable); 3067 }else{ 3068 foundInMetaData = true; 3069 isContinue = false; 3070 } 3071 3072 }else if ((lcTable.getTableType() == ETableSource.subquery) 3073 ||((lcTable.getTableType() == ETableSource.openquery)&&(lcTable.getSubquery() != null))){ 3074 3075 lcResult = lcTable.searchColumnInAlias(pColumn)>=0; 3076 if (!lcResult){ 3077 lcResult = lcTable.getSubquery().searchColumnInResultSet(pColumn,(tables.size() == 1) 3078 &&(pColumn.getCandidateTables().size() == 0)); 3079 if (! lcResult) { 3080 candidateTableCnt++; 3081 pColumn.getCandidateTables().addTable(lcTable); 3082 } 3083 } 3084 3085 3086// if (lcTable.isIncludeColumnAlias()){ 3087// // System.out.println("subquery with alias:"+lcTable.getAliasClause().toString()+", skip search:"+pColumn.toString()); 3088// 3089// }else{ 3090// lcResult = lcTable.getSubquery().searchColumnInResultSet(pColumn,(tables.size() == 1)&&(pColumn.getCandidateTables().size() == 0)); 3091// if (! lcResult) candidateTableCnt++; 3092// } 3093 }else if (lcTable.isCTEName()){ 3094 lcResult = lcTable.getCTE().searchColumnInResultSet(this,lcTable,pColumn,tables.size() == 1); 3095 if (! lcResult) { 3096 candidateTableCnt++; 3097 pColumn.getCandidateTables().addTable(lcTable); 3098 } 3099 }else if (lcTable.getTableType() == ETableSource.function){ 3100 // search in this table function 3101 if(tables.size() == 1){ 3102 int lcInTableFunction = lcTable.getFuncCall().isColumnInThisTableFunction(this.getSqlEnv(),this.dbvendor,pColumn); 3103 lcResult = ( lcInTableFunction != TBaseType.COLUMN_IN_TABEL_FUNCTION_NO); 3104 // ClickHouse: an unverifiable table-function column 3105 // (NOTSURE) must not outrank an expression-CTE alias 3106 // visible in an enclosing scope — the alias is proven, 3107 // the guessed column is not. Leaving it unmatched lets 3108 // the parent-walk resolve the alias. 3109 if (lcResult && (lcInTableFunction == TBaseType.COLUMN_IN_TABEL_FUNCTION_NOTSURE) 3110 && (dbvendor == EDbVendor.dbvclickhouse) && (!pColumn.isQualified()) 3111 && (findExpressionCteForAliasInAnyScope(pColumn) != null)){ 3112 lcResult = false; 3113 } 3114 } 3115 else{ 3116 lcResult = ( lcTable.getFuncCall().isColumnInThisTableFunction(this.getSqlEnv(),this.dbvendor,pColumn) 3117 == TBaseType.COLUMN_IN_TABEL_FUNCTION_YES); 3118 } 3119 }else if (lcTable.getTableType() == ETableSource.tableExpr 3120 && lcTable.getTableExpr().getExpressionType() == EExpressionType.function_t 3121 && lcTable.getTableExpr().getFunctionCall() != null){ 3122 // search in this table function 3123 lcResult = ( lcTable.getTableExpr().getFunctionCall().isColumnInThisTableFunction(this.getSqlEnv(),this.dbvendor,pColumn) 3124 == TBaseType.COLUMN_IN_TABEL_FUNCTION_YES); 3125 }else if (lcTable.getTableType() == ETableSource.pivoted_table){ 3126 lcResult = fireOnMetaDatabaseTableColumn( 3127 lcTable.getPrefixServer() 3128 ,lcTable.getPrefixDatabase() 3129 ,lcTable.getPrefixSchema() 3130 ,lcTable.getName() 3131 ,pColumn.getColumnNameOnly()); 3132 if (lcResult){ 3133 foundInMetaData = true; 3134 isContinue = false; 3135 } 3136 }else if (lcTable.getTableType() == ETableSource.unnest){ 3137 for(TObjectName objectName:lcTable.getLinkedColumns()){ 3138 if (SQLUtil.compareIdentifier(this.dbvendor, ESQLDataObjectType.dotColumn, objectName.toString(), pColumn.toString())){ 3139 lcResult = true; 3140 break; 3141 } 3142 } 3143 3144 if (!lcResult){ 3145 if (lcTable.getAliasClause() == null){ 3146 // this unnest() clause generate column with default name: "value" 3147 if (pColumn.toString().equalsIgnoreCase("value")){ 3148 lcResult = true; 3149 } 3150 }else{ 3151 } 3152 } 3153 }//unnest 3154 } 3155 3156 if (lcResult) { 3157 lcTable.getLinkedColumns().addObjectName(pColumn); 3158 pColumn.setSourceTable(lcTable); 3159 // A real table binding in this scope outranks an alias mark a 3160 // wider-scope pre-pass may have left (ClickHouse closest-scope). 3161 pColumn.setExpressionCteRef(null); 3162 // pColumn.setValidate_column_status(TBaseType.COLUMN_LINKED_TO_TABLE_IN_OLD_ALGORITHM); 3163 if (!isContinue) break; 3164 } 3165 } 3166 3167 if ((lcResult) && (foundInMetaData)) return true; 3168 3169 // check variable after metadata checking 3170 if (locateVariableOrParameter(pColumn)) return false; 3171 3172 // check if this is the column alias in current select list. 3173// if((!lcResult)&& ((!pColumn.isPrefixed()) && (this instanceof TSelectSqlStatement)&&(getResultColumnList() !=null))){ 3174// for(int j=0;j<getResultColumnList().size();j++){ 3175// TResultColumn lcField = getResultColumnList().getResultColumn(j); 3176// lcResult = lcField.isMatchedUsingAlias(pColumn); 3177// if ((lcResult)&&(pColumn.getStartToken().posinlist > lcField.getAliasClause().getStartToken().posinlist)){ 3178// pColumn.setSourceColumn(lcField); 3179// lcField.getTargetColumns().addObjectName(pColumn); 3180// break; 3181// }else{ 3182// lcResult = false; 3183// } 3184// } 3185// } 3186 3187 if (lcResult) return true; 3188 3189 boolean isSearchUpLevel = (this.parentStmt != null); 3190 3191 if ((isSearchUpLevel) && (sqlstatementtype == ESqlStatementType.sstselect)){ 3192 isSearchUpLevel = (pColumn.isQualified() 3193 || ( 3194// (((TSelectSqlStatement)(this)).getLocation() != ESqlClause.elTable) && 3195 (! ((TSelectSqlStatement)(this)).isQueryOfCTE()) 3196 ) 3197 ) 3198 && (parentStmt.sqlstatementtype != ESqlStatementType.sstinsert) 3199 && (!((pColumn.getLocation() == ESqlClause.selectList)&&(((TSelectSqlStatement)(this)).getLocation() == ESqlClause.join))) 3200 && ((((TSelectSqlStatement)(this)).getLocation() != ESqlClause.pivot_in)) 3201 && (!((parentStmt.sqlstatementtype == ESqlStatementType.sstcreatetable))) 3202 && (!((parentStmt.sqlstatementtype == ESqlStatementType.sstcreateview))) 3203// && (!((pColumn.getLocation() == ESqlClause.selectList)&&(parentStmt.sqlstatementtype == ESqlStatementType.sstcreatetable))) 3204// && (!((pColumn.getLocation() == ESqlClause.selectList)&&(parentStmt.sqlstatementtype == ESqlStatementType.sstcreateview))) 3205 && (! ((pColumn.getLocation() == ESqlClause.selectList) 3206 &&(candidateTableCnt == 1) && (this instanceof TSelectSqlStatement) 3207 && (((TSelectSqlStatement)(this)).getLocation() == ESqlClause.elTable) 3208 ) ) // ref:mantis: #2628 3209 // && ( ((TSelectSqlStatement)(this.parentStmt)).getSetOperatorType() == ESetOperatorType.none) 3210 ; 3211 3212 if (isSearchUpLevel){ 3213 isSearchUpLevel = !((!pColumn.isQualified())&&(((TSelectSqlStatement) this).getLocation() == ESqlClause.where)); 3214 } 3215 } 3216 3217 if (isSearchUpLevel&&(pColumn.isContinueToSearch())){ // only search one level up, c:\prg\gsp_sqlfiles\TestCases\java\oracle\dbobject\berger_sqltest_04.sql 3218 boolean increaseLevel = true; 3219 if (parentStmt instanceof TSelectSqlStatement){ 3220 if( ((TSelectSqlStatement)parentStmt).getSetOperatorType() != ESetOperatorType.none){ 3221 increaseLevel = false; 3222 } 3223 } 3224 if (increaseLevel){ 3225 pColumn.searchLevel++; 3226 } 3227 3228 lcResult = parentStmt.linkColumnToTable(pColumn,pLocation); 3229 3230 if (increaseLevel){ 3231 pColumn.searchLevel--; 3232 } 3233 } 3234 3235 if ((! lcResult) && (pColumn.searchLevel == 0)) { 3236 if (this.sqlstatementtype == ESqlStatementType.sstselect){ 3237 if( ((TSelectSqlStatement)this).getSetOperatorType() == ESetOperatorType.none){ 3238 // USING _spVV0 (INTEGER) 3239 // INSERT INTO table3 3240 // SELECT :_spVV0,x. *,m.col3 3241 // from (( select table1.col1, (table1.col1 + table5.col2) c from table1 3242 // union all select col3,col4 from table2) x 3243 // cross join (select id from table2) m ) 3244 3245 // table5 in the above sql only link to the nearest level sql, but not to up-level which is union all 3246 3247 linkToFirstTable(pColumn,candidateTableCnt); 3248 } 3249 }else{ 3250 linkToFirstTable(pColumn,candidateTableCnt); 3251 } 3252 } 3253 3254 return lcResult; 3255 } 3256 3257 3258 /** 3259 * 3260 * @deprecated As of v1.6.0.1, use {@link #linkColumnToTable} instead 3261 */ 3262 /** 3263 * True when {@code cr} is an Oracle pseudocolumn rather than a reference to 3264 * a column of a table, judged only on what the statement itself proves. 3265 * 3266 * <p>The name list lives in {@link OraclePseudoColumnUtil}; this method 3267 * supplies the statement context that decides the non-reserved names. 3268 * Nothing here consults a catalog, so names that are indistinguishable from 3269 * a real column without one - {@code ORA_ROWSCN}, {@code OBJECT_ID}, 3270 * {@code OBJECT_VALUE}, {@code XMLDATA}, {@code COLUMN_VALUE} - are 3271 * deliberately not claimed. {@code OBJECT_ID} in particular is a real 3272 * column of {@code ALL_OBJECTS}, and claiming it would silently drop that 3273 * lineage.</p> 3274 * 3275 * @param cr the column reference being linked 3276 * @return true if {@code cr} is provably a pseudocolumn 3277 */ 3278 protected boolean isOraclePseudoColumnReference(TObjectName cr){ 3279 if (dbvendor != EDbVendor.dbvoracle) return false; 3280 if (cr == null) return false; 3281 // getColumnNameOnly() is the raw text of the LAST segment, quotes 3282 // included, so a quoted "ROWID" simply fails to match the name list - 3283 // which is the correct outcome, since "ROWID" and ROWID are different 3284 // names in Oracle. Do NOT add a getQuoteType() guard here: that method 3285 // reports the quote state of the FIRST segment, so it would reject 3286 // "seq".NEXTVAL, whose terminal segment is unquoted and really is the 3287 // sequence pseudocolumn. 3288 String nameOnly = cr.getColumnNameOnly(); 3289 if (nameOnly == null || nameOnly.length() == 0) return false; 3290 3291 // Reserved words with no owning table (ROWNUM, LEVEL). Oracle rejects 3292 // an unquoted column of these names, so no qualifier or clause can make 3293 // this a real column reference. ROWID is deliberately NOT here: it is 3294 // scoped to one table, keeps its attribution, and is retyped in 3295 // TObjectName.setSourceTable() instead. 3296 if (OraclePseudoColumnUtil.isDetachedReservedPseudoColumn(nameOnly)) return true; 3297 3298 // CONNECT_BY_ISLEAF / CONNECT_BY_ISCYCLE are not reserved. They are 3299 // pseudocolumns only inside a hierarchical query, and only unqualified: 3300 // Oracle has no table to qualify them with, so t.CONNECT_BY_ISLEAF is a 3301 // reference to a column somebody created called CONNECT_BY_ISLEAF. 3302 if (OraclePseudoColumnUtil.isConnectByPseudoColumn(nameOnly) 3303 && cr.getTableToken() == null 3304 && hasHierarchicalQueryClause()) return true; 3305 3306 // VERSIONS_* are only pseudocolumns when the table they come from 3307 // carries a flashback VERSIONS clause. 3308 if (OraclePseudoColumnUtil.isVersionsPseudoColumn(nameOnly) 3309 && hasFlashbackVersionsClause(cr.getTableString())) return true; 3310 3311 return false; 3312 } 3313 3314 /** 3315 * True when {@code cr} is {@code [schema.]sequence.NEXTVAL} or 3316 * {@code [schema.]sequence.CURRVAL}. 3317 * 3318 * <p>Neither name is reserved, so this needs the qualifier to be something 3319 * other than a table or alias in scope: {@code x.CURRVAL} where {@code x} 3320 * is a table alias is an ordinary reference to a column named 3321 * {@code CURRVAL}, and an unqualified {@code CURRVAL} likewise.</p> 3322 * 3323 * <p>Kept separate from {@link #isOraclePseudoColumnReference} because the 3324 * two get different treatment - see the call site.</p> 3325 * 3326 * @param cr the column reference being linked 3327 * @return true if {@code cr} is a sequence pseudocolumn 3328 */ 3329 protected boolean isOracleSequencePseudoColumnReference(TObjectName cr){ 3330 if (dbvendor != EDbVendor.dbvoracle) return false; 3331 if (cr == null) return false; 3332 // See isOraclePseudoColumnReference(): quote state is segment-specific 3333 // and already carried by getColumnNameOnly(); getQuoteType() reads the 3334 // wrong segment for a qualified name. 3335 String nameOnly = cr.getColumnNameOnly(); 3336 if (nameOnly == null || nameOnly.length() == 0) return false; 3337 return OraclePseudoColumnUtil.isSequencePseudoColumn(nameOnly) 3338 && cr.getTableToken() != null 3339 && !isTableOrAliasInScope(cr.getTableString()); 3340 } 3341 3342 /** 3343 * True when this statement (or the statement it belongs to) carries an 3344 * Oracle {@code CONNECT BY}. 3345 */ 3346 private boolean hasHierarchicalQueryClause(){ 3347 // Deliberately NOT walking up to the parent statement. CONNECT BY 3348 // pseudocolumns belong to the query that carries the clause; they are 3349 // not visible to a subquery nested inside it. Walking up would claim 3350 // CONNECT_BY_ISLEAF in 3351 // SELECT (SELECT CONNECT_BY_ISLEAF FROM other) FROM t CONNECT BY ... 3352 // where it is an ordinary column of "other", and drop that edge. 3353 if (this instanceof TSelectSqlStatement){ 3354 return ((TSelectSqlStatement)this).getHierarchicalClause() != null; 3355 } 3356 return false; 3357 } 3358 3359 /** 3360 * True when any table in scope carries a flashback {@code VERSIONS BETWEEN} 3361 * clause, which is what makes the {@code VERSIONS_*} pseudocolumns 3362 * available. 3363 */ 3364 private boolean hasFlashbackVersionsClause(String qualifier){ 3365 // Same scoping rule as hasHierarchicalQueryClause(): the VERSIONS_* 3366 // pseudocolumns come from a table in THIS statement's FROM clause, so 3367 // no parent-chain walk. An inner subquery of a version query does not 3368 // inherit them. 3369 if (tables == null) return false; 3370 // When the reference names a table, only THAT table's clause counts. 3371 // In "FROM x VERSIONS BETWEEN ... , y", y.VERSIONS_XID is an ordinary 3372 // column of y - y is not being version-queried. 3373 if (qualifier != null && qualifier.length() > 0){ 3374 int idx = tables.searchTableByNameOrAlias(qualifier); 3375 if (idx < 0) return false; 3376 TFlashback flashback = tables.getTable(idx).getFlashback(); 3377 return flashback != null && flashback.isVersionsQuery(); 3378 } 3379 for (int i = 0; i < tables.size(); i++){ 3380 TFlashback flashback = tables.getTable(i).getFlashback(); 3381 if (flashback != null && flashback.isVersionsQuery()) return true; 3382 } 3383 return false; 3384 } 3385 3386 /** 3387 * True when {@code name} matches a table name or alias visible to this 3388 * statement. Used to tell {@code sequence.CURRVAL} from 3389 * {@code tablealias.CURRVAL}. 3390 */ 3391 private boolean isTableOrAliasInScope(String name){ 3392 if (name == null || name.length() == 0) return false; 3393 TCustomSqlStatement stmt = this; 3394 while (stmt != null){ 3395 if (stmt.tables != null && stmt.tables.searchTableByNameOrAlias(name) >= 0) return true; 3396 stmt = stmt.getParentStmt(); 3397 } 3398 return false; 3399 } 3400 3401 public void linkColumnReferenceToTable(TObjectName cr, ESqlClause plocation){ 3402 // this is the column name, link it to table 3403 if (cr == null) return; 3404 cr.setLocation(plocation); 3405 if (cr.getObjectType() == TObjectName.ttobjVariable) return; 3406 if (cr.getObjectType() == TObjectName.ttobjColumnAlias) return; 3407 if ((this.dbvendor == EDbVendor.dbvsybase)||(this.dbvendor == EDbVendor.dbvsybasease || this.dbvendor == EDbVendor.dbvsqlanywhere || this.dbvendor == EDbVendor.dbvsybaseiq)){ 3408 TSourceToken pt = cr.getPartToken(); 3409 if ( pt != null){ 3410 if (pt.tokentype == ETokenType.ttdqstring){ 3411 //"0123", quoted string start with a number can't a column 3412 if ((pt.toString().charAt(1) >= '0') 3413 &&(pt.toString().charAt(1) <= '9')){ 3414 return; 3415 }else if (pt.toString().length() == 2){ 3416 //"", empty 3417 return; 3418 }else if (pt.toString().substring(1,pt.toString().length()-1).trim().length() == 0){ 3419 //" " 3420 return; 3421 } 3422 } 3423 } 3424 } 3425 3426 3427 if (cr.getPartToken() != null){ 3428 if (cr.getPartToken().tokentype == ETokenType.ttkeyword){ 3429 boolean reservedKeyword = false; 3430 switch (dbvendor){ 3431 case dbvmssql: 3432 //reservedKeyword = ! this.getGsqlparser().getFlexer().canBeColumnName(cr.getPartToken().tokencode); 3433 reservedKeyword = ! TLexerMssql.canBeColumnName(cr.getPartToken().tokencode); 3434 break; 3435 case dbvsybase: 3436 reservedKeyword = keywordChecker.isKeyword(cr.getPartToken().toString(), EDbVendor.dbvsybase, "15.7", true); 3437 break; 3438 case dbvsybasease: 3439 // Shares dbvsybase's keyword DATA file (it is the ASE 3440 // reserved-word list); independent case block per HC-5. 3441 reservedKeyword = keywordChecker.isKeyword(cr.getPartToken().toString(), EDbVendor.dbvsybase, "15.7", true); 3442 break; 3443 case dbvsqlanywhere: 3444 // Same shared keyword DATA file; independent case per HC-5. 3445 reservedKeyword = keywordChecker.isKeyword(cr.getPartToken().toString(), EDbVendor.dbvsybase, "15.7", true); 3446 break; 3447 case dbvsybaseiq: 3448 // Same again; independent case per HC-5. 3449 reservedKeyword = keywordChecker.isKeyword(cr.getPartToken().toString(), EDbVendor.dbvsybase, "15.7", true); 3450 break; 3451 default: 3452 break; 3453 } 3454 if (reservedKeyword) return; 3455 } 3456 } 3457 3458 // let's check is this columnreference is variable or parameter of plsql function/procedure 3459 // if (locateVariableOrParameter(cr)) return; 3460 3461// if ((cr.getPartToken() != null)&&((dbvendor == EDbVendor.dbvmssql)||(dbvendor == EDbVendor.dbvsybase))){ 3462// if ((cr.getPartToken().tokentype == ETokenType.ttkeyword)&&(!(this.getGsqlparser().getFlexer().canBeColumnName(cr.getPartToken().tokencode)))){ 3463// // keyword can't be column name: 3464// //select * From dbo.table Where DATEDIFF(day, create_date, expiry_date) < 14 3465// return; 3466// } 3467// } 3468 3469 if ((cr.toString().startsWith("@"))) 3470// if ((cr.toString().endsWith("*"))||(cr.toString().startsWith("@"))) 3471 { 3472 cr.setObjectType(TObjectName.ttobjNotAObject); 3473 return; 3474 } 3475 3476 if (dbvendor == EDbVendor.dbvoracle){ 3477 if ( //(cr.toString().compareToIgnoreCase ("rowid") == 0)|| 3478 (cr.toString().compareToIgnoreCase ("sysdate") == 0) 3479 || (cr.toString().compareToIgnoreCase ("nextval") == 0) 3480 || (cr.toString().compareToIgnoreCase ("rownum") == 0) 3481 || (cr.toString().compareToIgnoreCase ("level") == 0) 3482 ){ 3483 cr.setObjectType(TObjectName.ttobjNotAObject); 3484 if (cr.getDbObjectType() == EDbObjectType.unknown){ 3485 cr.setDbObjectType(EDbObjectType.notAColumn); 3486 } 3487 return; 3488 } 3489 // The comparisons above are against the whole rendered text, so they 3490 // only ever fire for a bare, unqualified name. Oracle documents 18 3491 // pseudocolumns and most of them reach here qualified (X.ROWID, 3492 // SEQ.CURRVAL) or under a spelling the list never had. Left alone 3493 // they fall through to checkColumnReferenceInTables() below and get 3494 // bound to a table in the FROM clause, which asserts that e.g. 3495 // ORA_ROWSCN is a column of that table (Mantis #4675). 3496 if (isOracleSequencePseudoColumnReference(cr)){ 3497 // [schema.]sequence.NEXTVAL|CURRVAL. Detach it from the FROM 3498 // clause but leave dbObjectType alone, which keeps CURRVAL on 3499 // the exact footing NEXTVAL has always had (unknown, no source 3500 // table). Promoting these to notAColumn would reclassify them 3501 // from column references to constants in DataFlowAnalyzer and 3502 // move existing lineage output; the defect reported was CURRVAL 3503 // being bound to an unrelated table, not its type. 3504 // Deliberately no setSourceTable(null) here: nothing has linked 3505 // this reference yet, and that setter has a side effect of 3506 // forcing dbObjectType to column, which is exactly the type we 3507 // are trying to leave alone. 3508 cr.setObjectType(TObjectName.ttobjNotAObject); 3509 cr.setValidate_column_status(TBaseType.VALIDATED_CAN_NOT_BE_A_COLUMN_NAME); 3510 return; 3511 } 3512 if (isOraclePseudoColumnReference(cr)){ 3513 cr.setObjectType(TObjectName.ttobjNotAObject); 3514 cr.setSourceTable(null); 3515 cr.setValidate_column_status(TBaseType.MARKED_NOT_A_COLUMN_IN_COLUMN_RESOLVER); 3516 cr.setDbObjectTypeDirectly(EDbObjectType.notAColumn); 3517 return; 3518 } 3519 } 3520 3521 if (((cr.toString().toUpperCase().startsWith("INSERTED"))||(cr.toString().toUpperCase().startsWith("DELETED")))&&(plocation == ESqlClause.output)&&(targetTable != null)){ 3522 targetTable.getObjectNameReferences().addObjectName(cr); 3523 return; 3524 } 3525 3526 if ( ((cr.toString().toUpperCase().startsWith(":NEW")) 3527 ||(cr.toString().toUpperCase().startsWith(":OLD"))) 3528 &&(this.getTopStatement() instanceof TPlsqlCreateTrigger) 3529 &&(dbvendor == EDbVendor.dbvoracle)){ 3530 this.getTopStatement().tables.getTable(0).getObjectNameReferences().addObjectName(cr); 3531 return; 3532 } 3533 3534 3535 3536 int ret = this.tables.checkColumnReferenceInTables(cr); 3537 if (ret >= 0) { 3538 TTable lcTable = this.tables.getTable(ret); 3539 if (lcTable.isBaseTable()){ 3540 lcTable.getObjectNameReferences().addObjectName(cr); 3541 }else if (lcTable.isCTEName()){ 3542 //WITH temp 3543 // AS (SELECT * 3544 // FROM sysibm.systables), 3545 // temp1 3546 // AS (SELECT * 3547 // FROM sysibm.syscolumns) 3548 //SELECT * 3549 //FROM temp A 3550 // INNER JOIN temp1 B 3551 // ON A.creator = B.tbcreator 3552 // AND A.name = B.tbname 3553 TCTE lccte = findCTEByName(lcTable.toString()); 3554 if (lccte != null){ 3555 TObjectName objectName = new TObjectName(); 3556 objectName.init(cr.getPartToken()); 3557 if (lccte.getSubquery() != null){ 3558 lccte.getSubquery().linkColumnReferenceToTable(objectName,plocation); 3559 } 3560 } 3561 }else if (lcTable.getTableType() == ETableSource.subquery){ 3562 // link s2t1a1 to subselect2table1 via s2 3563 //select 3564 // s2.s2t1a1 3565 //from 3566 // ( 3567 // select s2t1.* 3568 // from subselect2table1 s2t1 3569 // ) s2 3570 TSelectSqlStatement subquery = lcTable.getSubquery(); 3571 3572 if(((subquery.getValueClause() == null))&&(!subquery.isCombinedQuery())&&(subquery.getResultColumnList() != null)&&(subquery.getResultColumnList().size() == 1)){ 3573 TResultColumn lcColumn = subquery.getResultColumnList().getResultColumn(0); 3574 if (lcColumn.toString().endsWith("*")){ 3575 boolean isfound = false; 3576 3577 for(int i=0;i<subquery.tables.size();i++){ 3578 if (subquery.tables.getTable(i).getTableType() == ETableSource.subquery) continue; 3579 String columnStr = null; 3580 if (cr.getPartToken() != null){ 3581 //cr.getObjectType() is not ttObjColumn, so we can't use 3582 // getColumnToken, this is a bug, need to check it later. 3583 columnStr = cr.getPartToken().toString(); 3584 } 3585 if (this.fireOnMetaDatabaseTableColumn( 3586 subquery.tables.getTable(i).getTableName().getServerString(), 3587 subquery.tables.getTable(i).getTableName().getDatabaseString(), 3588 subquery.tables.getTable(i).getTableName().getSchemaString(), 3589 subquery.tables.getTable(i).getName(),columnStr)){ 3590 subquery.tables.getTable(i).getObjectNameReferences().addObjectName(cr); 3591 isfound = true; 3592 break; 3593 } 3594 } 3595 3596 3597 3598 if (!isfound) 3599 { 3600 if(subquery.tables.size() > 1){ 3601 cr.setTableDetermined(false); 3602 } 3603 for(int i=0;i<subquery.tables.size();i++){ 3604 subquery.tables.getTable(i).getObjectNameReferences().addObjectName(cr); 3605 } 3606 } 3607 3608 } // "*" 3609 } 3610 } 3611 }else if (ret == -2){ 3612 // no qualifier before column, check is this column of a cte, if not,set it to non-cte table 3613 boolean isfound = false; 3614 for (int i=0;i<this.tables.size();i++){ 3615 if ((this.tables.getTable(i).isCTEName()) &&(this.tables.getTable(i).getCteColomnReferences() != null)){ 3616 if (this.tables.getTable(i).getCteColomnReferences().searchColumnReference(cr) >= 0){ 3617 this.tables.getTable(i).getObjectNameReferences().addObjectName(cr); 3618 isfound = true; 3619 break; 3620 } 3621 } 3622 } 3623 3624 // no qualifier before column, but we still need to check uplevel table like this: 3625 //SELECT 3626 // col1 , 3627 // 3628 // ( SELECT col2 3629 // FROM tab1 3630 // WHERE col2 = col1 ) 3631 // FROM tab2 3632 // we need to link col1 to tab2 in up level, but not to tab1 3633 if ((!isfound) &&( 3634 (cr.getLocation() != ESqlClause.resultColumn) 3635 &&(cr.getLocation() != ESqlClause.insertColumn) 3636 &&(cr.getLocation() != ESqlClause.mergeInsert) 3637 &&(cr.getLocation() != ESqlClause.selectList) 3638 ) ){ // code #111 3639 TCustomSqlStatement lcParent = null; 3640 lcParent = this.getParentStmt(); 3641 while ( lcParent != null) { 3642 TTable lcTable; 3643 //ret = lcParent.tables.checkColumnReferenceInTables(cr); 3644 if (lcParent.sqlstatementtype != ESqlStatementType.sstselect) { 3645 break; 3646 } 3647 for (int i=0;i<lcParent.tables.size();i++){ 3648 lcTable = lcParent.tables.getTable(i); 3649 if (lcTable.getTableType() == ETableSource.objectname) { 3650 for(int k = 0; k< lcTable.getObjectNameReferences().size();k++){ 3651 if (lcTable.getObjectNameReferences().getObjectName(k).isTableDetermined()){ 3652 if (SQLUtil.compareIdentifier(this.dbvendor, ESQLDataObjectType.dotColumn, cr.toString(), lcTable.getObjectNameReferences().getObjectName(k).toString())){ 3653 isfound = true; 3654 break; 3655 } 3656 } 3657 } 3658 if (isfound) break; 3659 } 3660 } 3661 3662 if (isfound){ 3663 break; 3664 }else{ 3665 lcParent = lcParent.getParentStmt(); 3666 } 3667 } // while 3668 3669 } // end of code #111 3670 3671 if (!isfound){ 3672 isfound = checkNonQualifiedColumnReferenceInSubQueryOfUplevelStmt(cr 3673 , ((plocation == ESqlClause.resultColumn) 3674 ||(plocation == ESqlClause.insertColumn) 3675 ||(plocation == ESqlClause.mergeInsert) 3676 ||(plocation == ESqlClause.selectList) 3677 ) 3678 ); 3679 } 3680 3681 if ((!isfound)&&(this.tables.size() > 0)){ 3682 int candidate = 0, firstCandidate = -1; 3683 // add this column reference to first non-cte( or cte with column list is null) and non-subquery table 3684 for (int i=0;i<this.tables.size();i++){ 3685 // no qualified column can't belong to a table with alias, that column must be qualified if it's belong to a table with alias 3686 //if (this.tables.getTable(i).aliasClause != null) continue; 3687 if ( 3688 ( 3689 (!this.tables.getTable(i).isCTEName()) 3690 ||((this.tables.getTable(i).isCTEName())&&(this.tables.getTable(i).getCteColomnReferences() == null)) 3691 )&&((this.tables.getTable(i).getTableType() != ETableSource.subquery)) 3692 ) 3693 { 3694 candidate++; 3695 if (firstCandidate == -1) firstCandidate = i; 3696 if (this.fireOnMetaDatabaseTableColumn( 3697 this.tables.getTable(i).getTableName().getServerString(), 3698 this.tables.getTable(i).getTableName().getDatabaseString(), 3699 this.tables.getTable(i).getTableName().getSchemaString(), 3700 this.tables.getTable(i).getName(),cr.toString())){ 3701 this.tables.getTable(i).getObjectNameReferences().addObjectName(cr); 3702 isfound = true; 3703 break; 3704 } 3705 else{ 3706 this.tables.getTable(i).getObjectNameReferences().addObjectName(cr); 3707 if (this.tables.size() > 1){ 3708 cr.setTableDetermined(false); 3709 } 3710 isfound = true; 3711 break; 3712 } 3713 } 3714 } 3715 if ((!isfound) && (candidate == 1)){ 3716 this.tables.getTable(firstCandidate).getObjectNameReferences().addObjectName(cr); 3717 } 3718 } 3719 }else if (ret == -1){ 3720 TCustomSqlStatement lcParent = null; 3721 lcParent = this.getParentStmt(); 3722 while ( lcParent != null) { 3723 ret = lcParent.tables.checkColumnReferenceInTables(cr); 3724 if (ret >= 0){ 3725 lcParent.tables.getTable(ret).getObjectNameReferences().addObjectName(cr); 3726 break; 3727 }else{ 3728 lcParent = lcParent.getParentStmt(); 3729 } 3730 } // while 3731 } //-1 3732 3733 } 3734 3735 /** 3736 * Found out is a non qualified column is a column in uplevel subquery table like this: 3737 * take ma_parkey for example: ma_parkey is not a physical column 3738 * 3739 SELECT c_mandant 3740 , CASE WHEN EXISTS (SELECT 1 3741 FROM CDS_H_GRUPPE GRP1 3742 WHERE GRP1.c_mandant = c_mandant 3743 AND GRP1.parkey1 = ma_parkey) 3744 THEN 1 3745 ELSE NULL 3746 END MA_ME 3747 FROM (SELECT c_mandant 3748 , CASE WHEN funktionscode = 'U' 3749 THEN parkey1 3750 ELSE parkey2 3751 END MA_PARKEY 3752 FROM 3753 CDS_H_GRUPPE 3754 ) 3755 */ 3756 public boolean checkNonQualifiedColumnReferenceInSubQueryOfUplevelStmt(TObjectName crf,boolean sameLevelOnly){ 3757 boolean ret = false; 3758 3759 TCustomSqlStatement lcParent = null; 3760 lcParent = this;//getParentStmt(); 3761 while ( lcParent != null) { 3762 TTable lcTable; 3763 for (int i=0;i<lcParent.tables.size();i++){ 3764 lcTable = lcParent.tables.getTable(i); 3765 3766 if ((lcTable.getTableType() != ETableSource.subquery)) {continue;} 3767 3768 ret = isColumnNameInSelectList(crf.toString(),lcTable.subquery); 3769 if (ret) {break;} 3770 3771 } 3772 if (ret) {break;} 3773 else{ 3774 if (sameLevelOnly){ 3775 lcParent = null; 3776 }else{ 3777 lcParent = lcParent.getParentStmt(); 3778 } 3779 } 3780 } // while 3781 3782 return ret; 3783 } 3784 3785 private boolean isColumnNameInSelectList(String pColumn, TSelectSqlStatement pSelect){ 3786 // Iterative rewrite of the original recursive descent. Provably identical 3787 // output: it visits exactly the same set of leaf (non-combined) SELECTs as 3788 // the recursion, in the same left-before-right order, and applies the 3789 // identical per-column test (alias match, else expr match, case-insensitive). 3790 // The only reason for the change is that UNION trees are left-leaning and 3791 // can be thousands of branches deep, so recursive descent risks 3792 // StackOverflowError (see repo CLAUDE.md: iterative UNION traversal). 3793 Deque<TSelectSqlStatement> pending = new ArrayDeque<>(); 3794 pending.push(pSelect); 3795 while (!pending.isEmpty()) { 3796 TSelectSqlStatement select = pending.pop(); 3797 if (select == null) { 3798 continue; 3799 } 3800 if (select.isCombinedQuery()) { 3801 // Push right then left so left is popped/processed first, matching 3802 // the recursion's "check left before right" order. 3803 pending.push(select.getRightStmt()); 3804 pending.push(select.getLeftStmt()); 3805 continue; 3806 } 3807 if (select.getResultColumnList() != null){ //if it's a db2 value row, then getResultColumnList() will be null 3808 TResultColumnList resultColumns = select.getResultColumnList(); 3809 for(int j=0;j<resultColumns.size();j++){ 3810 TResultColumn lcColumn = resultColumns.getResultColumn(j); 3811 boolean ret = false; 3812 if (lcColumn.getAliasClause() != null){ 3813 ret = SQLUtil.compareIdentifier(this.dbvendor, ESQLDataObjectType.dotColumn, pColumn, lcColumn.getAliasClause().toString()); 3814 } 3815 if (ret) return true; 3816 ret = pColumn.equalsIgnoreCase(lcColumn.getExpr().toString()); 3817 if (ret) return true; 3818 } 3819 } 3820 } 3821 return false; 3822 } 3823 3824 public TCustomSqlStatement getTopStatement(){ 3825 TCustomSqlStatement ret = this; 3826 while (ret.getParentStmt() != null){ 3827 ret = ret.getParentStmt(); 3828 } 3829 return ret; 3830 } 3831 3832 3833// public String toScript(){ 3834// if (!isChanged()){ 3835// return this.toString(); 3836// } 3837// return super.toScript(); 3838// } 3839 3840} 3841 3842class constantVisitor extends TParseTreeVisitor { 3843 private boolean inWhere = false,inExprList = false; 3844 public void preVisit(TWhereClause node){ 3845 inWhere = true; 3846 } 3847 3848 public void postVisit(TWhereClause node){ 3849 inWhere = false; 3850 } 3851 3852 public void preVisit(TExpression node){ 3853 if (inWhere){ 3854 switch (node.getExpressionType()){ 3855 case list_t: 3856 inExprList = true; 3857 boolean isNumber = true; 3858 if (node.getExprList().size() > 0){ 3859 // check the type of the constant in the expr list 3860 TExpression expr = node.getExprList().getExpression(0); 3861 if (expr.getExpressionType() == EExpressionType.simple_constant_t){ 3862 if (expr.getConstantOperand().getLiteralType() == ELiteralType.etString){ 3863 isNumber = false; 3864 } 3865 } 3866 } 3867 3868 TSourceToken lcStartToken = node.getStartToken(); 3869 TSourceToken lcEndToken = node.getEndToken(); 3870 int tokenPos = 0; 3871 if ((lcEndToken != null) && (lcStartToken != null)){ 3872 TSourceToken lcCurrentToken = lcStartToken; 3873 while (lcCurrentToken != null){ 3874 3875 if (lcCurrentToken.equals(lcEndToken)){ 3876 break; 3877 }else{ 3878 3879 if (tokenPos == 1){ 3880 if (isNumber){ 3881 lcCurrentToken.setTextWithBackup("999"); 3882 }else{ 3883 lcCurrentToken.setTextWithBackup("'placeholder_str'"); 3884 } 3885 }else if (tokenPos > 1){ 3886 lcCurrentToken.tokenstatus = ETokenStatus.tsdeleted; 3887 } 3888 3889 lcCurrentToken = lcCurrentToken.getNextTokenInChain(); 3890 tokenPos++; 3891 } 3892 } 3893 } 3894 3895 break; 3896 } 3897 } // where 3898 } 3899 3900 public void postVisit(TExpression node){ 3901 if (inWhere){ 3902 switch (node.getExpressionType()){ 3903 case list_t: 3904 inExprList = false; 3905 break; 3906 } 3907 } 3908 } 3909 3910 public void preVisit(TConstant node){ 3911 if (inWhere&&(!inExprList)){ 3912 switch (node.getLiteralType()){ 3913 case etNumber: 3914 case etFloat: 3915 node.getStartToken().setTextWithBackup("999"); 3916 break; 3917 case etString: 3918 node.getStartToken().setTextWithBackup("'placeholder_str'"); 3919 break; 3920 } 3921 } 3922 } 3923 3924 public void preVisit(TFunctionCall node){ 3925 if (TBaseType.as_canonical_f_decrypt_replace_password){ 3926 int i = TBaseType.searchCryptFunction(node.getFunctionName().toString()); 3927 3928 if (i>0){ // find this function 3929 if (node.getArgs().size() >= i){ 3930 TExpression secondArg = node.getArgs().getExpression(i-1); 3931 if (secondArg.getExpressionType() == EExpressionType.simple_constant_t){ 3932 TConstant constant = secondArg.getConstantOperand(); 3933 constant.getValueToken().setTextWithBackup("'***'"); 3934 //System.out.println(node.toString()+":"+constant.toString()); 3935 }else if (secondArg.getExpressionType() == EExpressionType.simple_object_name_t){ 3936 TObjectName objectName = secondArg.getObjectOperand(); 3937 objectName.getStartToken().setTextWithBackup("'***'"); 3938 //System.out.println(node.toString()+":"+constant.toString()); 3939 } 3940 } 3941 } 3942 3943 } 3944 } 3945 3946 void processConstant(TConstant node){ 3947 switch (node.getLiteralType()){ 3948 case etNumber: 3949 case etFloat: 3950 node.getStartToken().setTextWithBackup("999"); 3951 break; 3952 case etString: 3953 node.getStartToken().setTextWithBackup("'placeholder_str'"); 3954 break; 3955 } 3956 } 3957 3958}