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