001package gudusoft.gsqlparser.nodes; 002 003import gudusoft.gsqlparser.*; 004import gudusoft.gsqlparser.compiler.TVariable; 005import gudusoft.gsqlparser.resolver.TColumnTableMatch; 006import gudusoft.gsqlparser.sqlenv.*; 007import gudusoft.gsqlparser.util.keywordChecker; 008 009import java.util.ArrayList; 010 011/** 012 * The qualified or unqualified name that identifies a database object. 013 * The qualified name may includes those parts: server,database,schema,object,part and dblink. 014 * This class represents database object in different database vendors such as Oracle, SQL Server in a uniform way. 015 * <p> 016 * The general syntax of database object in Oracle: [schema.]object[.part][@dblink] 017 * <p> 018 * The general syntax of database object in SQL Server: [server.][database.][schema.]object 019 * <p> 020 * The meaning of {@link #getObjectToken()} and {@link #getPartToken()} depends on the {@link #getDbObjectType()}. 021 * If this database object is a schema object such as table, index, then the objectToken represents this 022 * database object and partToken is null. 023 * <p><p> 024 * If this TObjectName represents a column, the partToken represents the column name, the objectToken is table/view 025 * name of this column if this column is qualified like <code>table.column</code>, otherwise, the objectToken is 026 * null. 027 * <p> 028 * schemaToken, databaseToken, serverToken is the qualified part of a database object name. 029 * If this objectName represents a database name in the create database statement like this 030 * <code>CREATE DATABASE menagerie</code>, then, the objectToken is menagerie and databaseToken is null. 031 * 032 * @see gudusoft.gsqlparser.EDbObjectType 033 **/ 034 035public class TObjectName extends TParseTreeNode implements Cloneable{ 036 037 public void setOwnStmt(TCustomSqlStatement ownStmt) { 038 this.ownStmt = ownStmt; 039 } 040 041 public TCustomSqlStatement getOwnStmt() { 042 return ownStmt; 043 } 044 045 private TCustomSqlStatement ownStmt; 046 047 048 private TExceptReplaceClause exceptReplaceClause; 049 050 public TExceptReplaceClause getExceptReplaceClause() { 051 return exceptReplaceClause; 052 } 053 054 public void setExceptReplaceClause(TExceptReplaceClause exceptReplaceClause) { 055 this.exceptReplaceClause = exceptReplaceClause; 056 } 057 058 private ETableKind tableKind = ETableKind.etkBase; 059 060 public void setTableKind(ETableKind tableKind) { 061 this.tableKind = tableKind; 062 } 063 064 public ETableKind getTableKind() { 065 return tableKind; 066 } 067 068 private EPseudoTableType pseudoTableType = EPseudoTableType.none; 069 070 public EPseudoTableType getPseudoTableType() { 071 return pseudoTableType; 072 } 073 074 public void setPseudoTableType(EPseudoTableType pseudoTableType) { 075 this.pseudoTableType = pseudoTableType; 076 } 077 078 TVariable linkedVariable = null; 079 080 public void setLinkedVariable(TVariable linkedVariable) { 081 this.linkedVariable = linkedVariable; 082 this.dbObjectType = EDbObjectType.variable; 083 } 084 085 public TVariable getLinkedVariable() { 086 return linkedVariable; 087 } 088 089 // 如果该对象表示一个字段,那么本属性表示该字段的数据来自那个源字段 090 private TAttributeNode sourceAttributeNode; 091 092 /** 093 * Sets the source attribute node for this object name. 094 * <p> 095 * This method is used only by the legacy TSQLResolver (RESOLVER). 096 * It is not used when using TSQLResolver2 (RESOLVER2) for name resolution. 097 * For RESOLVER2, use {@link #setResolution(gudusoft.gsqlparser.resolver2.model.ResolutionResult)} instead. 098 * 099 * @param sourceAttributeNode the attribute node representing the resolved source 100 * @deprecated Since 3.4.0.5. Use TSQLResolver2 with {@link #setResolution} instead. 101 */ 102 @Deprecated 103 public void setSourceAttributeNode(TAttributeNode sourceAttributeNode) { 104 this.sourceAttributeNode = sourceAttributeNode; 105 } 106 107 /** 108 * Returns the source attribute node for this object name. 109 * <p> 110 * This method is used only by the legacy TSQLResolver (RESOLVER). 111 * It is not used when using TSQLResolver2 (RESOLVER2) for name resolution. 112 * For RESOLVER2, use {@link #getResolution()} instead. 113 * 114 * @return the attribute node representing the resolved source, or null if not resolved 115 * @deprecated Since 3.4.0.5. Use TSQLResolver2 with {@link #getResolution()} instead. 116 */ 117 @Deprecated 118 public TAttributeNode getSourceAttributeNode() { 119 return sourceAttributeNode; 120 } 121 122 // ===== New Resolver2 fields ===== 123 /** 124 * Resolution result from the new resolver (resolver2). 125 * Contains complete resolution status, column source, and candidate information. 126 * This is set by gudusoft.gsqlparser.resolver2.NameResolver 127 */ 128 private gudusoft.gsqlparser.resolver2.model.ResolutionResult resolution; 129 130 /** 131 * Side-channel hint for deep struct field access (3+ part names without alias). 132 * Unlike resolution, this does NOT trigger sourceTable binding or affect lineage topology. 133 * Set by NameResolver for BigQuery 3-part no-alias struct patterns. 134 * 135 * @see gudusoft.gsqlparser.resolver2.model.StructFieldHint 136 */ 137 private gudusoft.gsqlparser.resolver2.model.StructFieldHint structFieldHint; 138 139 /** 140 * Set resolution result (called by resolver2.NameResolver) 141 */ 142 public void setResolution(gudusoft.gsqlparser.resolver2.model.ResolutionResult resolution) { 143 this.resolution = resolution; 144 145 // Synchronize sourceTable with resolution result 146 // sourceTable represents the IMMEDIATE source table (subquery, CTE, or physical table) 147 // NOT the final physical table after tracing through subqueries/CTEs 148 if (resolution != null && resolution.getStatus() == gudusoft.gsqlparser.resolver2.ResolutionStatus.EXACT_MATCH) { 149 gudusoft.gsqlparser.resolver2.model.ColumnSource source = resolution.getColumnSource(); 150 if (source != null) { 151 // Get the immediate source table from the namespace that resolved this column 152 gudusoft.gsqlparser.resolver2.namespace.INamespace sourceNs = source.getSourceNamespace(); 153 TTable immediateSource = (sourceNs != null) ? sourceNs.getSourceTable() : null; 154 155 // For star columns, preserve the existing sourceTable set by linkColumnToTable 156 // Star columns represent ALL columns from all tables in FROM clause 157 String colName = this.getColumnNameOnly(); 158 if (colName != null && colName.equals("*")) { 159 // Don't update sourceTable for star columns 160 } else if (immediateSource != null) { 161 // Special case: if Phase 1 (linkColumnToTable) already resolved to a physical table, 162 // and Phase 2 resolves to a CTE/subquery, check if they're referring to the same 163 // underlying table. If so, preserve Phase 1's physical table reference. 164 // This handles cases like non-recursive CTEs where a table reference inside the CTE 165 // definition should resolve to the physical table, not the CTE itself. 166 boolean preservePhase1 = false; 167 if (this.sourceTable != null && immediateSource != this.sourceTable) { 168 // Phase 1 already set sourceTable 169 ETableSource phase1Type = this.sourceTable.getTableType(); 170 boolean phase1IsPhysical = (phase1Type == ETableSource.objectname); 171 172 // Check if Phase 2 resolved to CTE/subquery 173 boolean phase2IsCTEOrSubquery = immediateSource.isCTEName() 174 || immediateSource.getTableType() == ETableSource.subquery; 175 176 if (phase1IsPhysical && phase2IsCTEOrSubquery) { 177 // Get finalTable from Phase 2 resolution 178 TTable finalTable = source.getFinalTable(); 179 // If finalTable matches Phase 1's sourceTable (same physical table), 180 // preserve Phase 1's result - BUT only for non-alias columns. 181 // For alias columns (e.g., SELECT t.id AS col1), the subquery layer 182 // must be preserved because the alias name doesn't exist in the physical table. 183 // We use getFinalColumnName() as a proxy: if it returns non-null, the column 184 // is an alias or passthrough-to-alias with a different name. 185 if (finalTable == this.sourceTable 186 && !source.isColumnAlias() 187 && source.getFinalColumnName() == null) { 188 preservePhase1 = true; 189 } 190 } 191 } 192 193 if (!preservePhase1) { 194 // Set sourceTable to the immediate source (subquery, CTE, or physical table) 195 this.sourceTable = immediateSource; 196 } 197 } 198 199 // Populate candidateTables from ColumnSource for UNION columns 200 // When getFinalTable() is null but we have candidate tables (from UNION branches), 201 // these should be tracked so the formatter can output all candidates 202 java.util.List<TTable> sourceCandidates = source.getCandidateTables(); 203 if (sourceCandidates != null && !sourceCandidates.isEmpty()) { 204 if (candidateTables == null) { 205 candidateTables = new TTableList(); 206 } 207 for (TTable candidate : sourceCandidates) { 208 if (candidate != null && !containsTable(candidateTables, candidate)) { 209 candidateTables.addTable(candidate); 210 } 211 } 212 // Check if candidates came from UNION/CTE propagation (not ambiguity) 213 // Evidence containing "union" or "propagate" indicates UNION branch tracing 214 String evidence = source.getEvidence(); 215 if (evidence != null && (evidence.contains("union") || evidence.contains("propagate"))) { 216 candidatesFromUnion = true; 217 } 218 } 219 220 // Sync propertyToken for struct field access (backward compatibility) 221 // When resolver2 detects struct field access (e.g., info.name where info is a RECORD column), 222 // call columnToProperty() to shift tokens so propertyToken contains the field name. 223 // 224 // To avoid false positives (e.g., treating table.column as column.field when table not found), 225 // we verify the base column is STRUCT type through: 226 // 1. Semantic analysis: Check if ColumnSource's definition node has STRUCT datatype 227 // 2. SQLEnv fallback: Check external metadata if semantic analysis inconclusive 228 // 3. TableNamespace verification: If base column comes from a physical table, it's STRUCT 229 // 230 // Method 3 is key: if "info" resolves to a column in a real table (TableNamespace), 231 // then "info.name" MUST be STRUCT field access (otherwise the SQL would be invalid). 232 // This enables pure SQL-based STRUCT detection without DDL or metadata. 233 if (source.isStructFieldAccess() && source.hasFieldPath()) { 234 String baseColumnName = source.getExposedName(); 235 boolean isVerifiedStructColumn = false; 236 237 // Method 1: Semantic analysis - check ColumnSource's definition node for STRUCT type 238 // This works when DDL (CREATE TABLE with STRUCT columns) is parsed in the same batch 239 TParseTreeNode definitionNode = source.getDefinitionNode(); 240 if (definitionNode instanceof TColumnDefinition) { 241 TColumnDefinition colDef = (TColumnDefinition) definitionNode; 242 TTypeName datatype = colDef.getDatatype(); 243 if (datatype != null && datatype.getDataType() == EDataType.struct_t) { 244 isVerifiedStructColumn = true; 245 } 246 } 247 248 // Method 2: SQLEnv fallback - check external metadata if semantic analysis didn't verify 249 if (!isVerifiedStructColumn && this.sqlEnv != null && baseColumnName != null && immediateSource != null) { 250 String tableName = immediateSource.getFullName(); 251 if (tableName != null) { 252 gudusoft.gsqlparser.sqlenv.TSQLTable sqlTable = this.sqlEnv.searchTable(tableName); 253 if (sqlTable != null) { 254 gudusoft.gsqlparser.sqlenv.TSQLColumn sqlColumn = sqlTable.getColumn(baseColumnName); 255 if (sqlColumn != null && sqlColumn.getColumnDataType() != null) { 256 EDataType dataType = sqlColumn.getColumnDataType().getDataType(); 257 isVerifiedStructColumn = (dataType == EDataType.struct_t); 258 } 259 } 260 } 261 } 262 263 // Method 3: TableNamespace verification - if base column comes from a physical table 264 // When struct field fallback finds the base column (e.g., "info") in a TableNamespace, 265 // the SQL semantic guarantees it's a STRUCT column. If "info" is a regular column in 266 // table "student_records", then "info.name" can ONLY mean STRUCT field access. 267 // This enables STRUCT detection purely from SELECT statement semantics. 268 // 269 // DISABLED: This optimization causes regressions in complex queries where column names 270 // overlap with table aliases (e.g., UNION subqueries with NULL AS column_name). 271 // Users can access field names via source.getFieldPath().getFirst() instead. 272 // TODO: Implement smarter heuristic to detect truly "clear" semantics. 273 // 274 // if (!isVerifiedStructColumn && sourceNs instanceof gudusoft.gsqlparser.resolver2.namespace.TableNamespace) { 275 // // Base column comes from a physical table - definitely STRUCT access 276 // isVerifiedStructColumn = true; 277 // } 278 279 // Only call columnToProperty() for verified STRUCT columns and non-star columns 280 if (isVerifiedStructColumn) { 281 String currentColName = getColumnNameOnly(); 282 if (currentColName == null || !currentColName.equals("*")) { 283 columnToProperty(); 284 } 285 } 286 } 287 } 288 } 289 290 // Sync resolveStatus for backward compatibility with legacy code (e.g., TGetTableColumn) 291 if (resolution != null) { 292 switch (resolution.getStatus()) { 293 case EXACT_MATCH: 294 this.resolveStatus = TBaseType.RESOLVED_AND_FOUND; 295 break; 296 case AMBIGUOUS: 297 this.resolveStatus = TBaseType.RESOLVED_BUT_AMBIGUOUS; 298 break; 299 case NOT_FOUND: 300 // Leave as NOT_RESOLVED_YET or whatever it was before 301 break; 302 } 303 } 304 } 305 306 /** 307 * Helper to check if a table is already in the TTableList 308 */ 309 private static boolean containsTable(TTableList list, TTable table) { 310 if (list == null || table == null) return false; 311 for (int i = 0; i < list.size(); i++) { 312 TTable existing = list.getTable(i); 313 if (existing == table) return true; 314 } 315 return false; 316 } 317 318 /** 319 * Get resolution result from new resolver 320 */ 321 public gudusoft.gsqlparser.resolver2.model.ResolutionResult getResolution() { 322 return resolution; 323 } 324 325 /** 326 * Convenience method: get column source (most common access pattern) 327 */ 328 public gudusoft.gsqlparser.resolver2.model.ColumnSource getColumnSource() { 329 if (resolution == null) return null; 330 331 if (resolution.getStatus() == gudusoft.gsqlparser.resolver2.ResolutionStatus.EXACT_MATCH) { 332 return resolution.getColumnSource(); 333 } else if (resolution.getStatus() == gudusoft.gsqlparser.resolver2.ResolutionStatus.AMBIGUOUS) { 334 // For ambiguous: return first candidate (configurable behavior) 335 gudusoft.gsqlparser.resolver2.model.AmbiguousColumnSource ambiguous = resolution.getAmbiguousSource(); 336 if (ambiguous != null && ambiguous.getCandidateCount() > 0) { 337 return ambiguous.getCandidates().get(0); 338 } 339 } 340 341 return null; 342 } 343 344 /** 345 * Get the struct field hint for deep struct access (3+ part names without alias). 346 * This is a side-channel annotation that does NOT affect resolution or sourceTable. 347 * 348 * @return The struct field hint, or null if not a deep struct access 349 */ 350 public gudusoft.gsqlparser.resolver2.model.StructFieldHint getStructFieldHint() { 351 return structFieldHint; 352 } 353 354 /** 355 * Set the struct field hint (called by NameResolver for 3+ part no-alias struct patterns). 356 */ 357 public void setStructFieldHint(gudusoft.gsqlparser.resolver2.model.StructFieldHint hint) { 358 this.structFieldHint = hint; 359 } 360 361 /** 362 * Check if this column reference is ambiguous 363 */ 364 public boolean isAmbiguous() { 365 return resolution != null && 366 resolution.getStatus() == gudusoft.gsqlparser.resolver2.ResolutionStatus.AMBIGUOUS; 367 } 368 369 /** 370 * Check if this column reference has been resolved (by new resolver) 371 */ 372 public boolean isResolved() { 373 return resolution != null && 374 resolution.getStatus() != gudusoft.gsqlparser.resolver2.ResolutionStatus.NOT_FOUND; 375 } 376 377 /** 378 * Get all candidate tables (for ambiguous columns) 379 */ 380 public java.util.List<TTable> getCandidateTables2() { 381 if (!isAmbiguous()) return java.util.Collections.emptyList(); 382 383 gudusoft.gsqlparser.resolver2.model.AmbiguousColumnSource ambiguous = resolution.getAmbiguousSource(); 384 return ambiguous.getCandidates().stream() 385 .map(gudusoft.gsqlparser.resolver2.model.ColumnSource::getFinalTable) 386 .filter(java.util.Objects::nonNull) 387 .collect(java.util.stream.Collectors.toList()); 388 } 389 // ===== End of New Resolver2 fields ===== 390// private boolean isResolved = false; 391// 392// public void setResolved(boolean resolved) { 393// isResolved = resolved; 394// } 395// 396// public void setResolvedRelation(TTable resolvedRelation) { 397// this.resolvedRelation = resolvedRelation; 398// this.isResolved = true; 399// } 400// 401// public boolean isResolved() { 402// return isResolved; 403// } 404// 405// public TTable getResolvedRelation() { 406// return resolvedRelation; 407// } 408// 409// private TTable resolvedRelation = null; 410 411 public TObjectName clone(){ 412 TObjectName cloneObject = new TObjectName(); 413 cloneObject.dbObjectType = this.dbObjectType; 414 cloneObject.dbvendor = this.dbvendor; 415 416 if (this.partToken != null){ 417 cloneObject.partToken = this.partToken.clone(); 418 } 419 if (this.objectToken != null){ 420 cloneObject.objectToken = this.objectToken.clone(); 421 } 422 if (this.schemaToken != null){ 423 cloneObject.schemaToken = this.schemaToken.clone(); 424 } 425 426 if (this.databaseToken != null){ 427 cloneObject.databaseToken = this.databaseToken.clone(); 428 } 429 430 if (this.serverToken != null){ 431 cloneObject.serverToken = this.serverToken.clone(); 432 } 433 434 if (this.propertyToken != null){ 435 cloneObject.propertyToken = this.propertyToken.clone(); 436 } 437 438 if (this.methodToken != null){ 439 cloneObject.methodToken = this.methodToken.clone(); 440 } 441 442 if (this.packageToken != null){ 443 cloneObject.packageToken = this.packageToken.clone(); 444 } 445 446 cloneObject.numberOfPart = this.numberOfPart; 447 448 // Clone additionalParts for deeply nested struct access 449 if (this.additionalParts != null && !this.additionalParts.isEmpty()) { 450 cloneObject.additionalParts = new java.util.ArrayList<>(); 451 for (TSourceToken token : this.additionalParts) { 452 cloneObject.additionalParts.add(token.clone()); 453 } 454 } 455 456 // Copy startToken and endToken from parent TParseTreeNode 457 if (this.getStartToken() != null) { 458 cloneObject.setStartTokenDirectly(this.getStartToken()); 459 } 460 if (this.getEndToken() != null) { 461 cloneObject.setEndTokenDirectly(this.getEndToken()); 462 } 463 464 return cloneObject; 465 } 466 467 private TObjectName parentObjectName; 468 469 public void setParentObjectName(TObjectName parentObjectName) { 470 this.parentObjectName = parentObjectName; 471 } 472 473 @Override 474 public TObjectName getParentObjectName() { 475 return parentObjectName; 476 } 477 478 public void setPath(TPathSqlNode path) { 479 this.path = path; 480 this.setEndToken(path.getEndToken()); 481 } 482 483 /** 484 * stage path 485 * 486 * @return 487 */ 488 public TPathSqlNode getPath() { 489 return path; 490 } 491 492 private TPathSqlNode path; 493 494// private TObjectName cursorName; 495// 496// public void setCursorName(TObjectName cursorName) { 497// this.cursorName = cursorName; 498// } 499// 500// /** 501// * related cursor name if oracle for statement 502// * like: FOR emp_rec IN emp_cur 503// * @return 504// */ 505// public TObjectName getCursorName() { 506// return cursorName; 507// } 508 509 private ArrayList<TTable> sourceTableList; 510 511 /** 512 * source table list for star column, 513 * <br>select * from emp,dept 514 * <br> * column will be list to both emp and dept table. 515 * <br> 516 * @return 517 */ 518 public ArrayList<TTable> getSourceTableList() { 519 if (sourceTableList == null) { 520 sourceTableList = new ArrayList<>(); 521 } 522 return sourceTableList; 523 } 524 525 private boolean isImplicitSchema = false; 526 private boolean isImplicitDatabase = false; 527 528 public boolean isImplicitSchema() { 529 return isImplicitSchema; 530 } 531 532 public boolean isImplicitDatabase() { 533 return isImplicitDatabase; 534 } 535 536// public void setOriginalQuery(TSelectSqlStatement originalQuery) { 537// this.originalQuery = originalQuery; 538// } 539// 540// public TSelectSqlStatement getOriginalQuery() { 541// return originalQuery; 542// } 543// 544// private TSelectSqlStatement originalQuery = null; 545 546 private ArrayList<TAttributeNode> attributeNodesDerivedFromFromClause; 547 548 /** 549 * 这个属性只有当 column 为 * 时有效 550 * 当 column 为 * 时, 本属性包含该 * 展开后对应的 attributeNode 列表,来源是 FROM CLAUSE中的 tables, 在 resolve star column 551 * 时给本属性赋值, TStmtScope.resolve(TObjectName objectName) 552 * 553 * 当 table 有metadata或DDL给出了明确的字段时,每table个展开的 attributeNode 包含明确的字段名,such as t.c 554 * 当 table 没有 metadata 和 DDL 时,每table个只展开的 一个 attributeNode,内容为 t.* 555 * 556 * 557 * @return 558 */ 559 public ArrayList<TAttributeNode> getAttributeNodesDerivedFromFromClause() { 560 if (attributeNodesDerivedFromFromClause == null){ 561 attributeNodesDerivedFromFromClause = new ArrayList<TAttributeNode>(); 562 } 563 return attributeNodesDerivedFromFromClause; 564 } 565 566 private ArrayList<TColumnTableMatch> candidateAttributeNodes; 567 568 /** 569 * 非 star column 使用该属性存放可能包含该 column 的 attributeNode 570 * star column 使用 {@link #getAttributeNodesDerivedFromFromClause()} 571 * 572 * @return 573 */ 574 public ArrayList<TColumnTableMatch> getCandidateAttributeNodes() { 575 if (candidateAttributeNodes == null){ 576 candidateAttributeNodes = new ArrayList<TColumnTableMatch>(); 577 } 578 return candidateAttributeNodes; 579 } 580 581 private TTableList candidateTables = null; 582 583 public TTableList getCandidateTables() { 584 if (candidateTables == null){ 585 candidateTables = new TTableList(); 586 } 587 return candidateTables; 588 } 589 590 /** True if candidateTables came from UNION/CTE branch propagation, not from ambiguity */ 591 private boolean candidatesFromUnion = false; 592 593 /** 594 * Returns true if candidate tables came from UNION/CTE branch propagation, 595 * false if they came from ambiguity (e.g., unqualified column in multi-table query). 596 * When true, the formatter should output all candidates instead of marking as "missed". 597 */ 598 public boolean isCandidatesFromUnion() { 599 return candidatesFromUnion; 600 } 601 602 /** 603 * Check DDL verification status for a candidate table. 604 * 605 * <p>Returns a tri-state result:</p> 606 * <ul> 607 * <li>1 = Column exists in table's DDL</li> 608 * <li>0 = Column NOT found in table's DDL (DDL available but column missing)</li> 609 * <li>-1 = Cannot verify (no DDL available for this table)</li> 610 * </ul> 611 * 612 * @param table The candidate table to check 613 * @return DDL verification status: 1 (exists), 0 (not found), -1 (no DDL) 614 */ 615 public int getDdlVerificationStatus(TTable table) { 616 String columnName = this.getColumnNameOnly(); 617 return gudusoft.gsqlparser.resolver2.model.ColumnSource.getDdlVerificationStatus(table, columnName); 618 } 619 620 /** 621 * Get DDL verification status for all candidate tables. 622 * 623 * <p>Returns a map from each candidate table to its DDL verification status:</p> 624 * <ul> 625 * <li>1 = Column exists in table's DDL</li> 626 * <li>0 = Column NOT found in table's DDL</li> 627 * <li>-1 = Cannot verify (no DDL available)</li> 628 * </ul> 629 * 630 * @return Map of candidate tables to their DDL verification status, or empty map if no candidates 631 */ 632 public java.util.Map<TTable, Integer> getCandidateTableDdlStatus() { 633 java.util.Map<TTable, Integer> result = new java.util.LinkedHashMap<>(); 634 if (candidateTables == null || candidateTables.size() == 0) { 635 return result; 636 } 637 638 String columnName = this.getColumnNameOnly(); 639 for (int i = 0; i < candidateTables.size(); i++) { 640 TTable candidate = candidateTables.getTable(i); 641 if (candidate != null) { 642 int status = gudusoft.gsqlparser.resolver2.model.ColumnSource.getDdlVerificationStatus(candidate, columnName); 643 result.put(candidate, status); 644 } 645 } 646 return result; 647 } 648 649 private boolean isOrphanColumn = false; 650 651 public void setOrphanColumn(boolean orphanColumn) { 652 isOrphanColumn = orphanColumn; 653 } 654 655 public boolean isOrphanColumn() { 656 return isOrphanColumn; 657 } 658 659 private boolean isReservedKeyword = false; 660 661 public boolean isReservedKeyword() { 662 return isReservedKeyword; 663 } 664 665 private TColumnDefinition linkedColumnDef = null; 666 667 public void setLinkedColumnDef(TColumnDefinition linkedColumnDef) { 668 this.linkedColumnDef = linkedColumnDef; 669 } 670 671 /** 672 * The column definition in create/alter table statement that include this column name object. 673 * <pre> 674 * CREATE TABLE table_name ( 675 * column1 datatype, 676 * column2 datatype 677 * ); 678 * </pre> 679 * In above SQL, <code>column1 datatype</code> is the column definition while <code>column1</code> is this 680 * object name. 681 * @return column definition in create/alter table statement 682 */ 683 public TColumnDefinition getLinkedColumnDef() { 684 685 return linkedColumnDef; 686 } 687 688 private TObjectName namespace; 689 690 public void setNamespace(TObjectName namespace) { 691 this.namespace = namespace; 692 } 693 694 /** 695 * The Couchbase namespace before keyspace 696 * @return the namespace 697 */ 698 public TObjectName getNamespace() { 699 700 return namespace; 701 } 702 703 /** 704 * 返回该对象名的字符串表示(中文说明): 705 * <p> 706 * 1) 优先调用父类 {@link TBaseType#toString()} 获取已构造好的整体标识文本; 707 * 若父类返回非空,则: 708 * - 对于 Snowflake,当文本以 <code>IDENTIFIER(...)</code> 形式出现时,仅提取并返回其中的 709 * 字面量部分(去除引号),以符合 Snowflake 标识符解析规则; 710 * - 其他情况直接返回父类结果。 711 * <p> 712 * 2) 若父类返回为空(尚未生成整体文本),则回退到更细粒度的 token: 713 * - 若存在 <code>part</code> 级 token,返回其字符串; 714 * - 否则若存在 <code>object</code> 级 token,返回其字符串; 715 * - 若仍不存在,返回 null。 716 * <p> 717 * 目的:统一并优先复用已构造的字符串表示,同时兼容特定数据库(如 Snowflake)的 718 * 标识符语义,从而在不同厂商 SQL 中提供稳定、符合预期的对象名输出。 719 */ 720 public String toString() { 721 String ret = super.toString(); 722 723 if (ret != null) { 724 if ((dbvendor == EDbVendor.dbvsnowflake) && (ret.toString().toLowerCase().startsWith("identifier("))){ 725 // snowflake identifier name: IDENTIFIER( { string_literal | session_variable | bind_variable | snowflake_scripting_variable } ) 726 // only return the string_literal part 727 // https://www.sqlparser.com/bugs/mantisbt/view.php?id=3566 728 // When parts have been split (mantis #4237), reconstruct the full qualified name 729 StringBuilder sb = new StringBuilder(); 730 if (getDatabaseString() != null && !getDatabaseString().isEmpty()) { 731 sb.append(getDatabaseString()).append("."); 732 } 733 if (getSchemaString() != null && !getSchemaString().isEmpty()) { 734 sb.append(getSchemaString()).append("."); 735 } 736 sb.append(getObjectString()); 737 return sb.toString(); 738 } 739 return ret; 740 } 741 742 if (getPartToken() != null) return getPartString(); 743 if (getObjectToken() != null ) return getObjectString(); 744 745 return null; 746 } 747 748 public void setQuoteType(EQuoteType quoteType) { 749 this.quoteType = quoteType; 750 } 751 752 /** 753 * Tell whether this is a quoted objectName. 754 * @return EQuoteType.squareBracket or EQuoteType.doubleQuote if this objectName is quoted. 755 */ 756 public EQuoteType getQuoteType() { 757 if (toString().startsWith("[")){ 758 return EQuoteType.squareBracket; 759 }else if (toString().startsWith("\"")){ 760 return EQuoteType.doubleQuote; 761 }else if (toString().startsWith("`")){ 762 return EQuoteType.backtick; 763 }else 764 return quoteType; 765 } 766 767 private EQuoteType quoteType = EQuoteType.notQuoted; 768// private String stringValue; 769 770 771 772 /** 773 * Internal use only 774 */ 775 public int searchLevel = 0; 776 777 public boolean isContinueToSearch(){ 778 // if column is in where clause, we can search 10 levels up 779 780 // if column is in select list, only select one level up. 781 // only search one level up, c:\prg\gsp_sqlfiles\TestCases\java\oracle\dbobject\berger_sqltest_04.sql 782 783 if (this.getLocation() == ESqlClause.where) return (searchLevel < 10); 784 else return (searchLevel < 1); 785 } 786 private TResultColumn sourceColumn; 787 788 /** 789 * Set the result column which include this column name. Used by parser internally. 790 * 791 * @param sourceColumn the result column includes this column name 792 */ 793 public void setSourceColumn(TResultColumn sourceColumn) { 794 this.sourceColumn = sourceColumn; 795 this.setDbObjectTypeDirectly(EDbObjectType.column); 796 } 797 798 /** 799 * Set the source column without changing dbObjectType. 800 * Used by resolver2 for legacy API compatibility when the column 801 * is already properly typed (e.g., star-inferred columns). 802 * 803 * @param sourceColumn the result column includes this column name 804 */ 805 public void setSourceColumnOnly(TResultColumn sourceColumn) { 806 this.sourceColumn = sourceColumn; 807 } 808 809 /** 810 * The result column which include this column 811 * <pre> 812 * select salary + 1000 from emp 813 * </pre> 814 * In the above SQL, <code>salary + 1000</code> is the result column while <code>salary</code> is this column name. 815 * 816 * @return the result column includes this column name 817 */ 818 public TResultColumn getSourceColumn() { 819 820 return sourceColumn; 821 } 822 823 /** 824 * The <b>immediate</b> source table where this column is visible in the current scope. 825 * 826 * <p>This represents the table/subquery/CTE that directly exposes this column in the FROM clause, 827 * NOT the final physical table after tracing through subqueries or CTEs.</p> 828 * 829 * <h3>Semantic Difference: sourceTable vs finalTable</h3> 830 * <ul> 831 * <li><b>sourceTable</b> (this field): The immediate/direct source in the current scope. 832 * For a column from a subquery, this points to the subquery's TTable.</li> 833 * <li><b>finalTable</b> (via {@code getResolution().getColumnSource().getFinalTable()}): 834 * The final physical table after tracing through all subqueries and CTEs.</li> 835 * </ul> 836 * 837 * <h3>Example</h3> 838 * <pre>{@code 839 * SELECT title FROM (SELECT * FROM books) sub 840 * 841 * For the 'title' column in outer SELECT: 842 * - sourceTable = TTable for subquery 'sub' (tableType=subquery) 843 * - finalTable = TTable for 'books' (the physical table) 844 * }</pre> 845 * 846 * @see #getSourceTable() 847 * @see gudusoft.gsqlparser.resolver2.model.ColumnSource#getFinalTable() 848 */ 849 private TTable sourceTable; 850 851 /** 852 * This column must be in this syntax: table.column, otherwise, this method always return false. 853 * Match tableToken with the input pTable, compare the alias of pTable to tableToken at first, 854 * If not the same, then compare the table name directly. This method can handle quoted name correctly. 855 * 856 * This method is used by parser internally. 857 * 858 * @param pTable table used to match {@link #getTableToken()} of this column object 859 * @return true if input table is matched with tableToken of this column object 860 */ 861 public boolean resolveWithThisTable(TTable pTable){ 862 boolean lcResult = false; 863 if (getTableString().length() == 0) return false; 864 if (pTable.getAliasName().length() > 0) { 865 lcResult = pTable.checkTableByName(getTableString().toString()); //pTable.getAliasName().toString().equalsIgnoreCase(getTableString().toString()); 866 if ((!lcResult)&&(getSchemaString().length()>0)&&(this.databaseToken == null)){ 867 // Only apply alias-to-schema matching for 3-part names (alias.column.field). 868 // For 4-part names (db.schema.table.column), databaseToken is set and the 869 // schema position IS the schema, not an alias. (Mantis #4268) 870 lcResult = pTable.getAliasName().toString().equalsIgnoreCase(getSchemaString().toString()); 871 if (lcResult){ 872 // table.column.field, table was recognized as schema in the parser, change the part token to property token 873 this.columnToProperty(); 874 } 875 } 876 } 877 if (lcResult) return true; 878 879 if (((pTable.isBaseTable()||(pTable.isCTEName()))&&(getTableToken() != null)&&(pTable.getTableName().getTableToken() != null))) { 880 // lcResult = getTableToken().toUnQuotedString().equalsIgnoreCase(pTable.getTableName().getTableToken().toUnQuotedString()); 881 String s1 = TBaseType.getTextWithoutQuoted(getTableToken().toString()); 882 String s2 = TBaseType.getTextWithoutQuoted(pTable.getTableName().getTableToken().toString()); 883// System.out.println("table1: "+s1); 884// System.out.println("table1: "+s2); 885 lcResult = s1.equalsIgnoreCase(s2); 886 887 if (lcResult && (!pTable.getPrefixDatabase().isEmpty()) && (!this.getDatabaseString().isEmpty()) && (!pTable.getPrefixDatabase().equalsIgnoreCase(this.getDatabaseString().toString()))) { 888 // teradata: UPDATE foodmart.STRTOK_TIME A SET SYSTEM_DESK = testdatabase.STRTOK_TIME.SYSTEM_DESK 889 // table STRTOK_TIME in testdatabase should be treat as the same one in foodmart 890 lcResult = false; 891 } 892 893 894 if ((!lcResult)&&(getSchemaString().length()>0)&&(this.databaseToken == null)){ 895 // Only apply table-name-to-schema matching for 3-part names (table.column.field). 896 // For 4-part names (db.schema.table.column), databaseToken is set and the 897 // schema position IS the schema, not a table name. (Mantis #4268) 898 // lcResult = pTable.getTableName().getTableToken().toUnQuotedString().equalsIgnoreCase(getSchemaString().toString()); 899 lcResult = TBaseType.getTextWithoutQuoted(pTable.getTableName().getTableToken().toString()).equalsIgnoreCase(getSchemaString().toString()); 900 if (lcResult){ 901 // table.column.field, table was recognized as schema in the parser, change the part token to property token 902 this.columnToProperty(); 903 } 904 } 905 } 906 return lcResult; 907 } 908 909 /** 910 * Check whether a column is prefixed by a table like this: <code>table.column</code> 911 * 912 * @return true if this column is in syntax like this: <code>table.column</code> 913 */ 914 public boolean isQualified(){ 915 return (getTableString().length() > 0); 916 } 917 918 public int getValidate_column_status() { 919 return validate_column_status; 920 } 921 922 public void setValidate_column_status(int validate_column_status) { 923 this.validate_column_status = validate_column_status; 924 } 925 926 private int validate_column_status = TBaseType.CAN_BE_COLUMN_NOT_VALIDATE_YET; 927 /** 928 * Check whether a column name is syntax valid in a specific database vendor. 929 * For example, in Oracle, <code>rowid</code> is not a valid column name. 930 * 931 * @param pDBVendor in which the database vendor the syntax of this column is checked 932 * @return true if this objectName can be used as a column name in the specified database 933 */ 934 public boolean isValidColumnName(EDbVendor pDBVendor){ 935 boolean lcResult = true; 936 if (validate_column_status == TBaseType.VALIDATED_CAN_BE_A_COLUMN_NAME) return true; 937 if (validate_column_status == TBaseType.VALIDATED_CAN_NOT_BE_A_COLUMN_NAME) return false; 938 if (validate_column_status == TBaseType.MARKED_NOT_A_COLUMN_IN_COLUMN_RESOLVER) return false; 939 if (validate_column_status == TBaseType.COLUMN_LINKED_TO_COLUMN_ALIAS_IN_OLD_ALGORITHM) return false; 940 941 942 943 if ((getObjectType() == TObjectName.ttobjVariable) 944 ||(getDbObjectType() == EDbObjectType.variable) 945 ||(getObjectType() == TObjectName.ttobjColumnAlias) 946 || (getDbObjectType() == EDbObjectType.xmlElement) 947 || (getDbObjectType() == EDbObjectType.date_time_part) 948 || (getDbObjectType() == EDbObjectType.constant) 949 || (getDbObjectType() == EDbObjectType.function) 950 ) { 951 validate_column_status = TBaseType.VALIDATED_CAN_NOT_BE_A_COLUMN_NAME; 952 return false; 953 } 954 955 // Numeric literals (e.g., "5" in LIMIT 5, "10" in TOP 10) are never valid column names. 956 // Some grammars wrap Number tokens in createObjectName() which causes them to flow through 957 // column linking. The lexer may assign ttkeyword or ttnumber as the token type depending 958 // on the vendor, so we check both the token type and the actual text content. 959 // This check is vendor-agnostic since no SQL dialect allows unquoted numeric literals 960 // as column names. 961 if (getPartToken() != null) { 962 TSourceToken pt = getPartToken(); 963 if (pt.tokentype == ETokenType.ttnumber) { 964 validate_column_status = TBaseType.VALIDATED_CAN_NOT_BE_A_COLUMN_NAME; 965 return false; 966 } 967 // Some lexers (e.g., Hive) assign ttkeyword to Number tokens. 968 // Check if the text is a numeric literal (integer or decimal). 969 String tokenText = pt.toString(); 970 if (tokenText.length() > 0 && isNumericLiteral(tokenText)) { 971 validate_column_status = TBaseType.VALIDATED_CAN_NOT_BE_A_COLUMN_NAME; 972 return false; 973 } 974 } 975 976 if (pDBVendor == EDbVendor.dbvsybase){ 977 TSourceToken pt = getPartToken(); 978 if ( pt != null){ 979 if (pt.tokentype == ETokenType.ttdqstring){ 980 //"0123", quoted string start with a number can't a column 981 if ((pt.toString().charAt(1) >= '0') 982 &&(pt.toString().charAt(1) <= '9')){ 983 lcResult = false; 984 }else if (pt.toString().length() == 2){ 985 //"", empty 986 lcResult = false; 987 }else if (pt.toString().substring(1,pt.toString().length()-1).trim().length() == 0){ 988 //" " 989 lcResult = false; 990 } 991 } 992 } 993 } 994 995 if (getPartToken() != null){ 996 if (getPartToken().tokentype == ETokenType.ttkeyword){ 997 998 switch (pDBVendor){ 999 case dbvmssql: 1000 //lcResult = this.getGsqlparser().getFlexer().canBeColumnName(getPartToken().tokencode); 1001 lcResult = TLexerMssql.canBeColumnName(getPartToken().tokencode); 1002 break; 1003 case dbvsybase: 1004 lcResult = !keywordChecker.isKeyword(getPartToken().toString(), EDbVendor.dbvsybase, "15.7", true); 1005 break; 1006 default: 1007 break; 1008 } 1009 } 1010 } 1011 1012 if ((toString().startsWith("@"))||((toString().startsWith(":"))&&(toString().indexOf(".")==-1))) 1013 { 1014 setObjectType(TObjectName.ttobjNotAObject); 1015 lcResult = false; 1016 } 1017 1018 switch (pDBVendor){ 1019 case dbvoracle: 1020 if ( //(getColumnNameOnly().compareToIgnoreCase ("rowid") == 0)|| 1021 (getColumnNameOnly().compareToIgnoreCase ("sysdate") == 0) 1022 || (getColumnNameOnly().compareToIgnoreCase ("nextval") == 0) 1023 || (getColumnNameOnly().compareToIgnoreCase ("rownum") == 0) 1024 || (getColumnNameOnly().compareToIgnoreCase ("level") == 0) 1025 || (getColumnNameOnly().compareToIgnoreCase ("user") == 0) 1026 ){ 1027 setObjectType(TObjectName.ttobjNotAObject); 1028 lcResult = false; 1029 } 1030 if ((toString().startsWith(":"))&&(toString().indexOf(".") == -1)) 1031 { // :bindv, but :new.column should not be enter here 1032 setObjectType(TObjectName.ttobjNotAObject); 1033 lcResult = false; 1034 } 1035 break; 1036 case dbvmssql: 1037 if ((getColumnNameOnly().compareToIgnoreCase ("system_user") == 0) 1038 ){ 1039 //setObjectType(TObjectName.ttobjNotAObject); 1040 lcResult = false; 1041 } 1042 break; 1043 case dbvmysql: 1044 if (toString().startsWith("\"")){ 1045 // "X" is a string literal 1046 lcResult = false; 1047 } 1048 if (keywordChecker.isKeyword(toString(),EDbVendor.dbvmysql,"6.0",true)){ 1049 isReservedKeyword = true; 1050 lcResult = false; 1051 } 1052 break; 1053 case dbvteradata: 1054 if ((getObjectString().length() == 0)&&((getColumnNameOnly().compareToIgnoreCase ("account") == 0) 1055 ||(getColumnNameOnly().compareToIgnoreCase ("current_date") == 0) 1056 ||(getColumnNameOnly().compareToIgnoreCase ("current_role") == 0) 1057 ||(getColumnNameOnly().compareToIgnoreCase ("current_time") == 0) 1058 ||(getColumnNameOnly().compareToIgnoreCase ("current_timestamp") == 0) 1059 ||(getColumnNameOnly().compareToIgnoreCase ("current_user") == 0) 1060 ||(getColumnNameOnly().compareToIgnoreCase ("database") == 0) 1061 ||((getColumnNameOnly().compareToIgnoreCase ("date") == 0)&&( this.getDbObjectType() != EDbObjectType.column )) 1062 ||(getColumnNameOnly().compareToIgnoreCase ("profile") == 0) 1063 ||(getColumnNameOnly().compareToIgnoreCase ("role") == 0) 1064 ||(getColumnNameOnly().compareToIgnoreCase ("session") == 0) 1065 ||(getColumnNameOnly().compareToIgnoreCase ("time") == 0) 1066 ||(getColumnNameOnly().compareToIgnoreCase ("user") == 0) 1067 ||(getColumnNameOnly().compareToIgnoreCase ("sysdate") == 0) 1068 )){ 1069 lcResult = false; 1070 } 1071 break; 1072 case dbvpostgresql: 1073 if (toString().startsWith("$")){ 1074 if ((toString().charAt(1) >= '0') 1075 &&(toString().charAt(1) <= '9')){ 1076 this.setDbObjectType(EDbObjectType.variable); 1077 lcResult = false; 1078 } 1079 } 1080 break; 1081 case dbvbigquery: 1082 if ((getColumnNameOnly().compareToIgnoreCase ("CURRENT_DATE") == 0) 1083 ||(getColumnNameOnly().compareToIgnoreCase ("CURRENT_TIME") == 0) 1084 ||(getColumnNameOnly().compareToIgnoreCase ("CURRENT_TIMESTAMP") == 0) 1085 ){ 1086 //setObjectType(TObjectName.ttobjNotAObject); 1087 lcResult = false; 1088 } 1089 break; 1090 } 1091 1092 if(lcResult){ 1093 validate_column_status = TBaseType.VALIDATED_CAN_BE_A_COLUMN_NAME; 1094 }else{ 1095 validate_column_status = TBaseType.VALIDATED_CAN_NOT_BE_A_COLUMN_NAME; 1096 } 1097 1098 return lcResult; 1099 1100 } 1101 1102 /** 1103 * Check if a string represents a numeric literal (integer or decimal). 1104 * Examples: "5", "10", "3.14", ".5" 1105 */ 1106 private static boolean isNumericLiteral(String text) { 1107 if (text == null || text.isEmpty()) return false; 1108 boolean hasDigit = false; 1109 boolean hasDot = false; 1110 for (int i = 0; i < text.length(); i++) { 1111 char c = text.charAt(i); 1112 if (c >= '0' && c <= '9') { 1113 hasDigit = true; 1114 } else if (c == '.' && !hasDot) { 1115 hasDot = true; 1116 } else { 1117 return false; 1118 } 1119 } 1120 return hasDigit; 1121 } 1122 1123 private boolean expandStarColumns = false; 1124 ArrayList<String> expandedStarColumns = new ArrayList<>(); 1125 1126 public ArrayList<String> getColumnsLinkedToStarColumn() { 1127 if (expandStarColumns) return expandedStarColumns; 1128 1129 //TTable sourceTable = this.getSourceTable(); 1130 if (getSourceTableList().size() > 0){ 1131 for( int i = 0;i < getSourceTableList().size();i++){ 1132 for(String c:getSourceTableList().get(i).getExpandedStarColumns()){ 1133 expandedStarColumns.add(c); 1134 } 1135 } 1136 } 1137 1138 expandStarColumns = true; 1139 return expandedStarColumns; 1140 1141 } 1142 1143 private ArrayList<String> columnsLinkedToStarColumn = new ArrayList<String>(); 1144 1145 private TResultColumnList columnsLinkedToStar; 1146 1147 public void setColumnsLinkedToStar(TResultColumnList columnsLinkedToStar) { 1148 this.columnsLinkedToStar = columnsLinkedToStar; 1149 for(TResultColumn rc:columnsLinkedToStar){ 1150 if (rc.getColumnAlias()!= ""){ 1151 columnsLinkedToStarColumn.add(rc.getColumnAlias()); 1152 }else{ 1153 columnsLinkedToStarColumn.add(rc.getColumnNameOnly()); 1154 } 1155 } 1156 1157 this.setDbObjectTypeDirectly(EDbObjectType.column); 1158 } 1159 1160 /** 1161 * if this is a star column(column name is *), and the value of this star column 1162 * is derived from a subquery, then, this field points to the select list in the subquery 1163 * 1164 * @return the select list in the subquery 1165 */ 1166 public TResultColumnList getColumnsLinkedToStar() { 1167 return columnsLinkedToStar; 1168 } 1169 1170 /** 1171 * Set the table this column belongs to. Used by parser internally. 1172 * 1173 * @param sourceTable table contains this column 1174 */ 1175 public void setSourceTable(TTable sourceTable) { 1176 1177 // INSERT INTO "omni"."omni_upload_t1a8067802b804755b1d29ee935b3b0bc" VALUES ($1, $2, $3, $4, $5) 1178 // if this objectname is $1, then just return 1179 if (this.getDbObjectType() == EDbObjectType.parameter) { 1180 // to avoid this parameter been checked in TAttributeResolver preVisit(TObjectName attribute) 1181 this.setValidate_column_status(TBaseType.COLUMN_LINKED_TO_TABLE_IN_OLD_ALGORITHM); 1182 return; 1183 } 1184 1185 this.sourceTable = sourceTable; 1186 // 如果 column token 的 tokentype 为 ETokenType.ttkeyword, 那么调整为 ETokenType.ttidentifier 1187 if ((this.getPartToken() != null)&&(this.getPartToken().tokentype == ETokenType.ttkeyword)){ 1188 if ((this.getPartToken().getDbObjectType() == EDbObjectType.column)||(this.getPartToken().getDbObjectType() == EDbObjectType.unknown)){ 1189 this.getPartToken().tokentype = ETokenType.ttidentifier; 1190 } 1191 } 1192 this.setDbObjectTypeDirectly(EDbObjectType.column); 1193 1194 // If this column was previously an orphan and is now linked to a table, 1195 // remove it from the orphanColumns list of its owning statement 1196 if (sourceTable != null && this.isOrphanColumn() && this.getOwnStmt() != null) { 1197 TObjectNameList orphanColumns = this.getOwnStmt().getOrphanColumns(); 1198 if (orphanColumns != null) { 1199 orphanColumns.removeElement(this); 1200 } 1201 this.setOrphanColumn(false); 1202 } 1203 } 1204 1205 public void setSourceTableBySQLResolver(TCustomSqlStatement sqlStatement, TAttributeNode attributeNode, TTable newSourceTable) { 1206 // 如果 column token 的 tokentype 为 ETokenType.ttkeyword, 那么调整为 ETokenType.ttidentifier 1207 if ((this.getPartToken() != null)&&(this.getPartToken().tokentype == ETokenType.ttkeyword)){ 1208 if ((this.getPartToken().getDbObjectType() == EDbObjectType.column)||(this.getPartToken().getDbObjectType() == EDbObjectType.unknown)){ 1209 this.getPartToken().tokentype = ETokenType.ttidentifier; 1210 } 1211 } 1212 1213 if ((this.sourceTable != null) && (this.sourceTable.equals(newSourceTable))) return; 1214 1215 if ((this.getResolveStatus() == TBaseType.RESOLVED_AND_FOUND ) 1216 || (newSourceTable.getTableType() != ETableSource.subquery)){// 关联到 subquery 的 column 本能算真正找到 table,因此还不能去掉 orphan column 1217 if (sqlStatement.getOrphanColumns().removeElement(this)){ 1218 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){ 1219 TBaseType.log(String.format("Remove orphan column <%s> find in old algorithm",this.toString()),TLog.WARNING,this); 1220 } 1221 // remove the waring in sql statement's error syntax list 1222 TCustomSqlStatement currentStatement = sqlStatement; 1223 while (currentStatement != null) { 1224 if (currentStatement.getSyntaxHints() != null) { 1225 for(int i=0; i<currentStatement.getSyntaxHints().size(); i++) { 1226 TSyntaxError syntaxError = currentStatement.getSyntaxHints().get(i); 1227 if (syntaxError.errortype == EErrorType.sphint) { 1228 if ((syntaxError.lineNo == this.getStartToken().lineNo)||(syntaxError.columnNo == this.getStartToken().columnNo)) { 1229 currentStatement.getSyntaxHints().remove(i); 1230 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){ 1231 TBaseType.log(String.format("Remove orphan column <%s> warning message in old algorithm", this.toString()), TLog.WARNING, this); 1232 } 1233 break; 1234 } 1235 } 1236 } 1237 } 1238 currentStatement = currentStatement.getParentStmt(); 1239 } 1240 } 1241 }else{ 1242 TBaseType.log(String.format("Found orphan column <%s> find in old algorithm in subquery %s, but NOT remove it from orphan list",this.toString(),newSourceTable.getAliasName()),TLog.WARNING,this); 1243 } 1244 1245 if (this.sourceTable != null){ 1246 if (this.sourceTable.getLinkedColumns().removeElement(this)){ 1247 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){ 1248 TBaseType.log(String.format("Remove <%s> at addr: %s from table <%s> that found in old algorithm, new linked table is: %s" 1249 ,this.toString(),Integer.toHexString(this.hashCode()),this.sourceTable.toString(),newSourceTable.toString()) 1250 ,TLog.WARNING,this); 1251 } 1252 } 1253 } 1254 this.sourceTable = newSourceTable; 1255 this.sourceTable.getLinkedColumns().addObjectName(this); 1256 1257 if (this.getSourceColumn() == null){ 1258 // if (attributeNode.isAttributeCreatedFromAliasColumn()){ 1259 this.setSourceColumn(attributeNode.getSubLevelResultColumn()); 1260 // } 1261 } 1262 1263 this.setDbObjectTypeDirectly(EDbObjectType.column); 1264 } 1265 1266 /** 1267 * Get the <b>immediate</b> source table where this column is visible in the current scope. 1268 * 1269 * <p>This returns the table/subquery/CTE that directly exposes this column in the FROM clause, 1270 * NOT the final physical table after tracing through subqueries or CTEs.</p> 1271 * 1272 * <p>To get the final physical table (after tracing through all layers), use: 1273 * {@code getResolution().getColumnSource().getFinalTable()}</p> 1274 * 1275 * <h3>Example</h3> 1276 * <pre>{@code 1277 * SELECT title FROM (SELECT * FROM books) sub 1278 * 1279 * For the 'title' column in outer SELECT: 1280 * - getSourceTable() → TTable for subquery 'sub' (tableType=subquery) 1281 * - resolution.getFinalTable() → TTable for 'books' (the physical table) 1282 * }</pre> 1283 * 1284 * @return The immediate source table, or null if not resolved 1285 * @see #sourceTable 1286 * @see gudusoft.gsqlparser.resolver2.model.ColumnSource#getFinalTable() 1287 */ 1288 public TTable getSourceTable() { 1289 // If new resolver determined this column is ambiguous, don't return old Phase 1 value 1290 // This ensures ambiguous columns are treated as orphan by the formatter 1291 // EXCEPTION: Star columns (*) should preserve their sourceTable for proper output 1292 if (resolution != null && resolution.isAmbiguous()) { 1293 String colName = getColumnNameOnly(); 1294 if (colName != null && colName.equals("*")) { 1295 // Star columns keep their Phase 1 sourceTable 1296 return sourceTable; 1297 } 1298 if (gudusoft.gsqlparser.TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE) { 1299 System.out.println("[TObjectName.getSourceTable] Column '" + colName + 1300 "' is AMBIGUOUS - returning null instead of " + 1301 (sourceTable != null ? sourceTable.getName() : "null")); 1302 } 1303 return null; 1304 } 1305 if (resolution == null && sourceTable != null) { 1306 if (gudusoft.gsqlparser.TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE) { 1307 System.out.println("[TObjectName.getSourceTable] Column '" + getColumnNameOnly() + 1308 "' has NO resolution - using Phase 1 sourceTable: " + sourceTable.getName()); 1309 } 1310 } 1311 return sourceTable; 1312 } 1313 1314 public void setResolveStatus(int resolveStatus) { 1315 this.resolveStatus = resolveStatus; 1316 1317 if (this.resolveStatus == TBaseType.RESOLVED_AND_FOUND){ 1318 this.setDbObjectTypeDirectly(EDbObjectType.column); 1319 } 1320 1321 } 1322 1323 private int resolveStatus = TBaseType.NOT_RESOLVED_YET; 1324 1325 public int getResolveStatus() { 1326 return resolveStatus; 1327 } 1328 1329 public void TObjectName(){ 1330 1331 } 1332 1333 private TObjectNameList columnAttributes = null; 1334 1335 private boolean subscripts; 1336 private TIndirection indirection; 1337 1338 /** 1339 * PostgreSQL column with array types 1340 * <pre> 1341 * CREATE TABLE sal_emp ( 1342 * name text, 1343 * pay_by_quarter integer[], 1344 * schedule text[][] 1345 * ); 1346 * </pre> 1347 * In the above SQL, this method returns true for <code>pay_by_quarter</code> column. 1348 * 1349 * @return true if this objectName is array type 1350 */ 1351 public boolean isSubscripts() { 1352 return subscripts; 1353 } 1354 1355 public void setIndirection(TIndirection indirection) { 1356 if(indirection == null) return; 1357 1358 this.indirection = indirection; 1359 // setup the exceptReplaceClause of the last indirection to the parent object which is set in the .y bnf file 1360 if (indirection.getIndices() != null){ 1361 if (indirection.getIndices().getElement(indirection.getIndices().size()-1).getAttributeName() != null){ 1362 setExceptReplaceClause(indirection.getIndices().getElement(indirection.getIndices().size()-1).getAttributeName().getExceptReplaceClause()); 1363 1364 } 1365 } 1366 1367 // possible syntax, support in postgresql only in current version: 1368 // [ indirection ], list in [] was indirection 1369 // 1370 // tablename[.column] 1371 // tablename[.*] 1372 // $1[.somecolumn] 1373 // 1374 // mytable[.arraycolumn[4]] 1375 // mytable[.two_d_column[17][34]] 1376 // $1[[10:42]] 1377 // 1378 1379 if (this.getObjectType() == TObjectName.ttobjPositionalParameters){ 1380 if(indirection.isRealIndices()){ 1381 //$1[10:42] 1382 this.subscripts = true; 1383 }else{ 1384 //$1.somecolumn 1385 this.setColumnTokenOfPositionalParameters(indirection.getIndices().getElement(0).getAttributeName().getPartToken()); 1386 } 1387 }else{ 1388 if(indirection.isRealIndices()){ 1389 if (indirection.getIndices().size() == 1){ 1390 // arraycolumn[4] 1391 this.subscripts = true; 1392 }else if (indirection.getIndices().size() >= 2){ 1393 if (!indirection.getIndices().getElement(0).isRealIndices()){ 1394 // mytable[.arraycolumn[4]] 1395 // mytable[.two_d_column[17][34]] 1396 // this.setPartTokenOfIndirection(indirection.getIndices().getElement(0).getAttributeName().getPartToken()); 1397 this.subscripts = true; 1398 // this.indirection.getIndices().remove(0); 1399 } 1400 } 1401 } 1402 1403 //else{ 1404 // 首先查找 : 和 [ 分隔符,如果找到,在该分隔符前的是 column,如果没有找到按照一般 qualified name 规则处理 1405 // https://docs.snowflake.com/en/user-guide/querying-semistructured.html 1406 int elementIndex = -1; 1407 for(int i=0;i<indirection.getIndices().size();i++){ 1408 TIndices tmp = indirection.getIndices().getElement(i); 1409 if ((tmp.getStartToken().tokencode == ':')||(tmp.getStartToken().tokencode == '[')||(tmp.getStartToken().tokencode == TBaseType.bind_v)){ 1410 elementIndex = i; 1411 break; 1412 } 1413 } 1414 if (elementIndex >= 0){ 1415 // 找到了 : 和 [ 分隔符 1416 if (elementIndex == 0){ 1417 // snowflake, <column>:<level1_element> 1418 // everything already perfect, nothing need to be changed 1419 partToken.setDbObjectType(EDbObjectType.column); 1420 }else if (elementIndex == 1){ 1421 // snowflake, table.column:<level1_element> 1422 objectToken = partToken; 1423 objectToken.setDbObjectType(EDbObjectType.table); 1424 partToken = indirection.getIndices().getElement(elementIndex-1).getAttributeName().getPartToken(); 1425 partToken.setDbObjectType(EDbObjectType.column); 1426 }else if (elementIndex == 2){ 1427 // snowflake, schema.table.column:<level1_element> 1428 schemaToken = partToken; 1429 schemaToken.setDbObjectType(EDbObjectType.schema); 1430 objectToken = indirection.getIndices().getElement(elementIndex-2).getAttributeName().getPartToken(); 1431 objectToken.setDbObjectType(EDbObjectType.table); 1432 partToken = indirection.getIndices().getElement(elementIndex-1).getAttributeName().getPartToken(); 1433 partToken.setDbObjectType(EDbObjectType.column); 1434 } 1435 }else{ 1436 // 一般 qualified name 规则处理 1437 if (indirection.getIndices().size() == 1){ 1438 this.setPartTokenOfIndirection(indirection.getIndices().getElement(0).getAttributeName().getPartToken()); 1439 }else if (indirection.getIndices().size() == 2){ 1440 schemaToken = partToken; 1441 schemaToken.setDbObjType(TObjectName.ttobjSchemaName); 1442 objectToken = indirection.getIndices().getElement(0).getAttributeName().getPartToken(); 1443 objectToken.setDbObjType(ttobjTable); 1444 partToken = indirection.getIndices().getElement(1).getAttributeName().getPartToken(); 1445 partToken.setDbObjType(ttobjColumn); 1446 }else if (indirection.getIndices().size() == 3){ 1447 // db.schema.tablename.column 1448 databaseToken = partToken; 1449 databaseToken.setDbObjectType(EDbObjectType.database); 1450 partToken = indirection.getIndices().getElement(2).getAttributeName().getPartToken(); 1451 partToken.setDbObjectType(EDbObjectType.column); 1452 objectToken = indirection.getIndices().getElement(1).getAttributeName().getPartToken(); 1453 objectToken.setDbObjectType(EDbObjectType.table); 1454 schemaToken = indirection.getIndices().getElement(0).getAttributeName().getPartToken(); 1455 schemaToken.setDbObjectType(EDbObjectType.schema); 1456 } 1457 } 1458 1459// if (indirection.getIndices().size() == 1){ 1460// if ((indirection.getIndices().getElement(0).getStartToken().tokencode == ':') 1461// ||(indirection.getIndices().getElement(0).getStartToken().tokencode == TBaseType.bind_v)) 1462// { 1463// // snowflake, <column>:<level1_element> 1464// 1465// }else{ 1466// // tablename[.column] 1467// // tablename[.*] 1468// this.setPartTokenOfIndirection(indirection.getIndices().getElement(0).getAttributeName().getPartToken()); 1469// } 1470// }else if (indirection.getIndices().size() == 2){ 1471// if ((indirection.getIndices().getElement(0).getStartToken().tokencode == ':') 1472// ||(indirection.getIndices().getElement(0).getStartToken().tokencode == TBaseType.bind_v)) 1473// { 1474// // snowflake, <column>:<level1_element> 1475// 1476// }else { 1477// // schema.tablename.column 1478// schemaToken = partToken; 1479// schemaToken.setDbObjType(TObjectName.ttobjSchemaName); 1480// objectToken = indirection.getIndices().getElement(0).getAttributeName().getPartToken(); 1481// objectToken.setDbObjType(ttobjTable); 1482// partToken = indirection.getIndices().getElement(1).getAttributeName().getPartToken(); 1483// partToken.setDbObjType(ttobjColumn); 1484// } 1485// }else if (indirection.getIndices().size() == 3){ 1486// // db.schema.tablename.column 1487// databaseToken = partToken; 1488// databaseToken.setDbObjectType(EDbObjectType.database); 1489// partToken = indirection.getIndices().getElement(2).getAttributeName().getPartToken(); 1490// partToken.setDbObjectType(EDbObjectType.column); 1491// objectToken = indirection.getIndices().getElement(1).getAttributeName().getPartToken(); 1492// objectToken.setDbObjectType(EDbObjectType.table); 1493// schemaToken = indirection.getIndices().getElement(0).getAttributeName().getPartToken(); 1494// schemaToken.setDbObjectType(EDbObjectType.schema); 1495// } 1496 1497 // } 1498 1499 } 1500 1501 } 1502 1503 /** 1504 * Array element of this objectName 1505 * <pre> 1506 * select arraycolumn[4] from t; 1507 * </pre> 1508 * In the above SQL, this method returns <code>[4]</code> of this objectName. 1509 * 1510 * @return array element of this objectName 1511 * @see gudusoft.gsqlparser.nodes.TIndirection 1512 */ 1513 public TIndirection getIndirection() { 1514 return indirection; 1515 } 1516 1517 private void setPartTokenOfIndirection(TSourceToken column){ 1518 parseTablename(); 1519 this.partToken = column; 1520 this.partToken.setDbObjType(ttobjColumn); 1521 } 1522 1523 public void setPropertyToken(TSourceToken propertyToken) { 1524 this.propertyToken = propertyToken; 1525 } 1526 1527 public TSourceToken getAtsign() { 1528 return atsign; 1529 } 1530 1531 public TSourceToken getMethodToken() { 1532 1533 return methodToken; 1534 } 1535 1536 public TSourceToken getPropertyToken() { 1537 return propertyToken; 1538 } 1539 1540 /** 1541 * The server part of this objectName: [server.][database.][schema.]object 1542 * 1543 * @return server part of the objectName 1544 */ 1545 public TSourceToken getServerToken() { 1546 return serverToken; 1547 } 1548 1549 public TSourceToken getExclamationmark() { 1550 1551 return exclamationmark; 1552 } 1553 1554 /** 1555 * 1556 * The database link part <code>remoreserver</code> in this objectName: scott.emp@remoreserver 1557 * 1558 * @return database link 1559 */ 1560 public TObjectName getDblink() { 1561 1562 return dblink; 1563 } 1564 1565 /** 1566 * The database part of this objectName: [server.][database.][schema.]object 1567 * 1568 * @return database part of the objectName 1569 */ 1570 public TSourceToken getDatabaseToken() { 1571 return databaseToken; 1572 } 1573 1574 1575 private boolean tableDetermined = true; 1576 1577 public void setTableDetermined(boolean tableDetermined) { 1578 this.tableDetermined = tableDetermined; 1579 } 1580 1581 1582 /** 1583 * Sometime, a non-qualified column can't be linked to a table without additional metadata from database. 1584 * <pre> 1585 * select name from emp, dept 1586 * </pre> 1587 * In the above SQL, the <code>name</code> column can't be determined which table it belongs to. 1588 * 1589 * Below is a more complicated SQL that shows the relationship between column and table. 1590 * <pre> 1591 * select 1592 * s2.s2t1a1, 1593 * s3.s3t1a1 1594 * from 1595 * ( 1596 * select * 1597 * from subselect2table1 s2t1 1598 * ) s2, 1599 * ( 1600 * select * 1601 * from subselect3table1, subselect3table2 1602 * ) s3 1603 * </pre> 1604 * 1605 * column s2t1a1 was linked to subselect2table1, {@link #isTableDetermined()} returns true for this column. 1606 * <br> column s3t1a1 was linked to both subselect3table1 and subselect3table2 1607 * due to lack of meta information from database, {@link #isTableDetermined()} returns false for this column. 1608 * <p> 1609 * Provide database metadata will help GSP links the column to the table correctly. 1610 * 1611 * @return true if this column can be linked to a table without doubt. 1612 * @see gudusoft.gsqlparser.TGSqlParser#setMetaDatabase 1613 */ 1614 public boolean isTableDetermined() { 1615 return tableDetermined; 1616 } 1617 1618 /** 1619 * used in Oracle and teradata SQL syntax 1620 * <p>teradata: 1621 * <p>column.attribute() 1622 * <p>column.attribute().attribute() 1623 * @param attributes 1624 */ 1625 public void attributesToPropertyToken(TObjectNameList attributes){ 1626 if (attributes.size() == 1){ 1627 this.propertyToken = attributes.getObjectName(0).getPartToken(); 1628 } 1629 } 1630 1631 public void setColumnAttributes(TObjectNameList columnAttributes) { 1632 this.columnAttributes = columnAttributes; 1633 } 1634 1635 /** 1636 * The data type of this column is structured UDT, this method returns the column's attributes. 1637 * Below is the sample SQL from <a href="https://info.teradata.com/HTMLPubs/DB_TTU_16_00/index.html#page/SQL_Reference/B035-1146-160K/fyj1472240813334.html">teradata</a>. 1638 * <pre> 1639 * 1640 * CREATE TYPE school_record AS ( 1641 * school_name VARCHAR(20), 1642 * GPA FLOAT); 1643 * 1644 * CREATE TYPE college_record AS ( 1645 * school school_record, 1646 * major VARCHAR(20), 1647 * minor VARCHAR(20)); 1648 * 1649 * CREATE TABLE student_record ( 1650 * student_id INTEGER, 1651 * Last_name VARCHAR(20), 1652 * First_name VARCHAR(20), 1653 * high_school school_record, 1654 * college college_record); 1655 * 1656 * SELECT student_id, last_name, first_name, 1657 * high_school.school_name(), high_school.GPA(), 1658 * college.school().school_name(), college.school().GPA(), 1659 * college.major(), college.minor() 1660 * FROM student_record; 1661 * 1662 * SELECT *.ALL FROM student_record; 1663 * SELECT student_record.*.ALL; 1664 * </pre> 1665 * Take this column <code>college.school().school_name()</code> for example, the partToken of this objectName 1666 * should be <code>college</code>, and the value returned by this method should be 1667 * <code>school().school_name()</code> 1668 * <p> 1669 * PLEASE NOTE THAT CURRENT VERSION CAN'T HANDLE THE ABOVE SQL CORRECTLY. 1670 * 1671 * @return attributes of this structured UDT column 1672 */ 1673 public TObjectNameList getColumnAttributes() { 1674 return columnAttributes; 1675 } 1676 1677 /** 1678 * Used internally. 1679 * @deprecated use {@link #setDbObjectType} instead 1680 * 1681 * @param objectType object type of this objectName 1682 */ 1683 public void setObjectType(int objectType) { 1684 if (this.objectType == objectType) return; 1685 this.objectType = objectType; 1686 // set this object type to source token 1687 switch(this.getObjectType()){ 1688 case TObjectName.ttobjTable: 1689 // case TObjectName.ttobjTableTemp: 1690 // case TObjectName.ttobjTableVar: 1691 this.parseTablename(); 1692 this.objectToken.setDbObjType(this.objectType); 1693 if (dbObjectType != EDbObjectType.stage){ // not already set to stage 1694 dbObjectType = EDbObjectType.table; 1695 } 1696 1697 if ((!TSQLEnv.supportSchema(this.dbvendor))&&(this.schemaToken != null)){ 1698 this.databaseToken = this.schemaToken; 1699 this.schemaToken = null; 1700 } 1701 break; 1702// case TObjectName.ttobjTableCTE: 1703// this.parseTablename(); 1704// this.objectToken.setDbObjType(this.objectType); 1705// dbObjectType = EDbObjectType.cte; 1706// break; 1707// case ttObjLibrary: 1708// this.parseTablename(); 1709// this.objectToken.setDbObjType(this.objectType); 1710// dbObjectType = EDbObjectType.library; 1711// break; 1712 case TObjectName.ttobjColumn: 1713 this.partToken.setDbObjType(this.objectType); 1714 dbObjectType = EDbObjectType.column; 1715 break; 1716 case TObjectName.ttobjColumnAlias: 1717 if ((this.objectToken == null) && (this.partToken != null)){ 1718 this.parseObjectName(); 1719 } 1720 this.objectToken.setDbObjType(this.objectType); 1721 dbObjectType = EDbObjectType.column_alias; 1722 break; 1723// case TObjectName.ttObjTableAlias: 1724// this.parseObjectName(); 1725// this.objectToken.setDbObjType(this.objectType); 1726// dbObjectType = EDbObjectType.table_alias; 1727// break; 1728 case TObjectName.ttobjParameter: 1729 this.parseObjectName(); 1730 this.objectToken.setDbObjType(this.objectType); 1731 dbObjectType = EDbObjectType.parameter; 1732 break; 1733 case TObjectName.ttobjVariable: 1734 this.parseVariableName(); 1735 this.objectToken.setDbObjType(this.objectType); 1736 dbObjectType = EDbObjectType.variable; 1737 break; 1738 case TObjectName.ttobjColumnMethod: 1739 if (dbObjectType != EDbObjectType.method){ 1740 this.parseColumnMethodName(); 1741 this.partToken.setDbObjType(this.ttobjColumn); 1742 this.methodToken.setDbObjType(this.ttobjColumnMethod); 1743 dbObjectType = EDbObjectType.method; 1744 } 1745 break; 1746// case TObjectName.ttobjProcedureName: 1747// this.parseFunctionName(); 1748// this.objectToken.setDbObjType(this.objectType); 1749// dbObjectType = EDbObjectType.procedure; 1750// break; 1751 case TObjectName.ttobjFunctionName: 1752 this.parseFunctionName(); 1753 this.objectToken.setDbObjType(this.objectType); 1754 dbObjectType = EDbObjectType.function; 1755 break; 1756// case TObjectName.ttobjLabelName: 1757// this.parseObjectName(); 1758// this.objectToken.setDbObjType(this.objectType); 1759// dbObjectType = EDbObjectType.label; 1760// break; 1761// case TObjectName.ttobjIndexName: 1762// this.parseObjectName(); 1763// this.objectToken.setDbObjType(this.objectType); 1764// dbObjectType = EDbObjectType.index; 1765// break; 1766// case TObjectName.ttobjMaterializedViewName: 1767// this.parseObjectName(); 1768// this.objectToken.setDbObjType(this.objectType); 1769// dbObjectType = EDbObjectType.materializedView; 1770// break; 1771// case TObjectName.ttobjViewName: 1772// this.parseObjectName(); 1773// this.objectToken.setDbObjType(this.objectType); 1774// dbObjectType = EDbObjectType.view; 1775// break; 1776// case TObjectName.ttobjCursorName: 1777// this.parseObjectName(); 1778// this.objectToken.setDbObjType(this.objectType); 1779// dbObjectType = EDbObjectType.cursor; 1780// break; 1781 case TObjectName.ttobjConstraintName: 1782 this.parseObjectName(); 1783 this.objectToken.setDbObjType(this.objectType); 1784 dbObjectType = EDbObjectType.constraint; 1785 break; 1786// case TObjectName.ttobjPropertyName: 1787// this.propertyToken.setDbObjType(this.objectType); 1788// dbObjectType = EDbObjectType.property; 1789// break; 1790// case TObjectName.ttobjTransactionName: 1791// this.parseObjectName(); 1792// this.objectToken.setDbObjType(this.objectType); 1793// dbObjectType = EDbObjectType.transaction; 1794// break; 1795// case TObjectName.ttobjDatabaseName: 1796// this.parseObjectName(); 1797// this.objectToken.setDbObjType(this.objectType); 1798// dbObjectType = EDbObjectType.database; 1799// break; 1800 case TObjectName.ttobjStringConstant: 1801 this.parseObjectName(); 1802 this.objectToken.setDbObjType(this.objectType); 1803 break; 1804// case TObjectName.ttobjAliasName: 1805// this.parseObjectName(); 1806// this.objectToken.setDbObjType(this.objectType); 1807// dbObjectType = EDbObjectType.alias; 1808// break; 1809 case TObjectName.ttobjAttribute: 1810 this.partToken.setDbObjType(this.objectType); 1811 dbObjectType = EDbObjectType.attribute; 1812 break; 1813 1814 case TObjectName.ttobjPositionalParameters: 1815 dbObjectType = EDbObjectType.parameter; 1816 break; 1817// case TObjectName.ttobjTypeName: 1818// this.parseObjectName(); 1819// this.objectToken.setDbObjType(this.objectType); 1820// dbObjectType = EDbObjectType.user_defined_type; 1821// break; 1822// case TObjectName.ttobjPackage: 1823// this.parseObjectName(); 1824// this.objectToken.setDbObjType(this.objectType); 1825// dbObjectType = EDbObjectType.plsql_package; 1826// break; 1827// case TObjectName.ttobjSequence: 1828// this.parseObjectName(); 1829// this.objectToken.setDbObjType(this.objectType); 1830// dbObjectType = EDbObjectType.sequence; 1831// break; 1832// case TObjectName.ttobjTrigger: 1833// this.parseObjectName(); 1834// this.objectToken.setDbObjType(this.objectType); 1835// dbObjectType = EDbObjectType.trigger; 1836// break; 1837 default: 1838 break; 1839 } 1840 } 1841 1842 public void setDbObjectType(EDbVendor dbVendor, EDbObjectType dbObjectType) { 1843 this.dbvendor = dbVendor; 1844 this.setDbObjectType(dbObjectType); 1845 } 1846 1847 public void setDbObjectTypeDirectly(EDbObjectType dbObjectType) { 1848 this.dbObjectType = dbObjectType; 1849 } 1850 /** 1851 * Set object type of this objectName 1852 * 1853 * @param dbObjectType database object type 1854 */ 1855 public void setDbObjectType(EDbObjectType dbObjectType) { 1856 if (this.dbObjectType == dbObjectType) return; 1857 if (this.dbObjectType == EDbObjectType.stage) return; 1858 // TODO, 如果已经被设定为某个对象类型,不应该再次设置,但如果下面的语句执行,会导致部分测试用例失败,需要查具体原因 1859 // if (this.dbObjectType != EDbObjectType.unknown) return; 1860 1861 EDbObjectType prev = this.dbObjectType; 1862 this.dbObjectType = dbObjectType; 1863 if (prev == EDbObjectType.unknown){ 1864 switch (dbObjectType){ 1865 case column: 1866 parseColumnName(); 1867 break; 1868 case table: 1869 case index: 1870 case synonym: 1871 case macro: 1872 case view: 1873 case stage: 1874 case task: 1875 case stream: 1876 case TEMP_TABLE: 1877 case pipe: 1878 case security_policy: 1879 1880 case plsql_package: 1881 case trigger: 1882 case transaction: 1883 case user_defined_type: 1884 case property: 1885 case cursor: 1886 case label: 1887 case table_alias: 1888 case partitionScheme: 1889 parseTablename(); 1890 break; 1891 case library: 1892 parseTablename(); 1893 break; 1894 case function: 1895 parseFunctionName(); 1896 break; 1897 case procedure: 1898 case materializedView: 1899 parseFunctionName(); 1900 break; 1901 case alias: 1902 case module: 1903 case sequence: 1904 parseTablename(); 1905 break; 1906 case database: 1907 this.objectToken = this.partToken; 1908 this.databaseToken = null; 1909 this.partToken = null; 1910 break; 1911 case variable: 1912 parseVariableName(); 1913 break; 1914 case schema: 1915 this.databaseToken = this.objectToken; 1916 this.objectToken = this.partToken; 1917 this.schemaToken = this.partToken; 1918 break; 1919 case method: 1920 this.parseColumnMethodName(); 1921 this.partToken.setDbObjType(this.ttobjColumn); 1922 //this.methodToken.setDbObjType(this.ttobjColumnMethod); 1923 this.methodToken.setDbObjectType(EDbObjectType.method); 1924 break; 1925 case cte: 1926 parseObjectName(); 1927 break; 1928 case hint: //sql server hint like nolock 1929 parseObjectName(); 1930 break; 1931 default: 1932 break; 1933 } 1934 } 1935 } 1936 1937 private EDbObjectType dbObjectType = EDbObjectType.unknown; 1938 1939 /** 1940 * The database object type of this objectName such as table, view, column for example. 1941 * If object type is {@link gudusoft.gsqlparser.EDbObjectType#column}, {@link #getPartToken} represents 1942 * the column name, for all other object type, the name of this database object is stored in {@link #getObjectToken} 1943 * 1944 * @return database object type 1945 */ 1946 public EDbObjectType getDbObjectType() { 1947 return dbObjectType; 1948 } 1949 1950 /** 1951 * @deprecated use {@link #getDbObjectType()} instead. 1952 * 1953 * @return the type of database object or variable this objectName represents for. 1954 */ 1955 public int getObjectType() { 1956 1957 return objectType; 1958 } 1959 1960 1961 public void setAtsign(TSourceToken atsign) { 1962 this.atsign = atsign; 1963 } 1964 1965 public void setDblink(TObjectName dblink) { 1966 dblink.setDbObjectType(EDbObjectType.dblink); 1967 this.dblink = dblink; 1968 } 1969 1970 public void setDblink(TObjectName dblink, boolean linkToDB) { 1971 setDblink(dblink); 1972 1973 if (linkToDB){ 1974 if (dblink.numberOfPart == 1){ 1975 this.databaseToken = dblink.getPartToken(); 1976 } 1977 } 1978 } 1979 1980 private TSourceToken serverToken = null; //sql server 1981 private TSourceToken databaseToken = null; //sql server 1982 // schemaToken.objectToken.partToken@dblink, schemaToken, partToken, and dblink is optional 1983 private TSourceToken schemaToken; 1984 private TSourceToken objectToken; 1985 1986 /* 1987 * part is a part of the object. This identifier lets you refer to a part of a schema object, 1988 * such as a column or a partition of a table. Not all types of objects have parts. 1989 */ 1990 private TSourceToken partToken; 1991 private TSourceToken propertyToken = null; 1992 private TSourceToken methodToken = null; 1993 private TSourceToken atsign; //@ 1994 private TObjectName dblink; 1995 1996 // Additional parts for deeply nested struct field access (BigQuery, etc.) 1997 // Stores parts beyond the 6 standard tokens (server, database, schema, object, part, property) 1998 private java.util.List<TSourceToken> additionalParts = null; 1999 2000 // ===== Phase 5: Normalized identifier cache (transient, not serialized) ===== 2001 // These caches reduce repeated normalize() calls for the same TObjectName 2002 private transient String normalizedServer; 2003 private transient String normalizedDatabase; 2004 private transient String normalizedSchema; 2005 private transient String normalizedTable; 2006 private transient String normalizedColumn; 2007 private transient long cacheFingerprint = 0; // Profile fingerprint for cache invalidation 2008 2009 public void setServerToken(TSourceToken serverToken) { 2010 this.serverToken = serverToken; 2011 } 2012 2013 public void setDatabaseToken(TSourceToken databaseToken, boolean implicit) { 2014 this.isImplicitDatabase = implicit; 2015 setDatabaseToken(databaseToken); 2016 } 2017 2018 public void setDatabaseToken(TSourceToken databaseToken) { 2019 this.databaseToken = databaseToken; 2020 } 2021 2022 public void setObjectToken(TSourceToken objectToken) { 2023 this.objectToken = objectToken; 2024 } 2025 2026 public void setPartToken(TSourceToken partToken) { 2027 this.partToken = partToken; 2028 } 2029 2030 public void setMethodToken(TSourceToken methodToken) { 2031 this.methodToken = methodToken; 2032 } 2033 2034 public void setSchemaToken(TSourceToken schemaToken, boolean implicit) { 2035 this.isImplicitSchema = implicit; 2036 setSchemaToken(schemaToken); 2037 } 2038 2039 public void setSchemaToken(TSourceToken schemaToken) { 2040 2041 this.schemaToken = schemaToken; 2042 } 2043 2044 public void setPackageToken(TSourceToken packageToken) { 2045 this.packageToken = packageToken; 2046 } 2047 2048 /** 2049 * Oracle package name 2050 * 2051 * @return the source token of Oracle package name. 2052 */ 2053 public TSourceToken getPackageToken() { 2054 return packageToken; 2055 } 2056 2057 private TSourceToken packageToken = null; 2058 2059 2060 /** 2061 * The object part of this objectName such as table name, view name. 2062 * 2063 * @return object part of this objectName 2064 */ 2065 public TSourceToken getObjectToken() { 2066 return objectToken; 2067 } 2068 2069 /** 2070 * The column name of this objectName if {@link #getDbObjectType} is {@link EDbObjectType#column}. 2071 * {@link #getColumnToken} returns the same value. 2072 * 2073 * @return the column name 2074 */ 2075 public TSourceToken getPartToken() { 2076 return partToken; 2077 } 2078 2079 /** 2080 * The schema name of this objectName. 2081 * 2082 * @return schema name 2083 */ 2084 public TSourceToken getSchemaToken() { 2085 return schemaToken; 2086 } 2087 2088 2089 private String schemaString; 2090 private String objectString; 2091 private String partString; 2092 2093 /** 2094 * String text of the package name. 2095 * 2096 * @return string of the package name,return null if empty. 2097 */ 2098 public String getPackageString(){ 2099 if (getPackageToken() != null) return getPackageToken().toString(); 2100 else return ""; 2101 } 2102 2103 /** 2104 * String text of the server name 2105 * 2106 * @return string of the server name,return null if empty. 2107 */ 2108 public String getServerString(){ 2109 if (getServerToken() != null) return getServerToken().toString(); 2110 else return ""; 2111 } 2112 2113 /** 2114 * String text of the database name 2115 * 2116 * @return string of the database name,return null if empty. 2117 */ 2118 public String getDatabaseString(){ 2119 if (isImplicitDatabase ) return ""; 2120 else if (getDatabaseToken() != null) return getDatabaseToken().toString(); 2121 else if ((this.dbObjectType == EDbObjectType.database) && (getObjectToken() != null)){ 2122 return getObjectToken().toString(); 2123 } 2124 else return ""; 2125 } 2126 2127 /** 2128 * String text of schema name in a qualified name of a schema object. 2129 * 2130 * 2131 * @return string of schema name, return null if empty. 2132 */ 2133 public String getSchemaString() { 2134 if (isImplicitSchema) return ""; 2135 else if (schemaToken != null) 2136 return schemaToken.getAstext(); 2137 else 2138 return "" ; 2139 } 2140 2141 private TSQLEnv sqlEnv = null; 2142 2143 public void setSqlEnv(TSQLEnv sqlEnv) { 2144 this.sqlEnv = sqlEnv; 2145 } 2146 2147 2148 2149 /** 2150 * This is the schema fetched from the SQLEnv. Not the direct qualified schema name of this object 2151 * search this table in the current default database and schema. 2152 * 2153 * If this is a qualified schema object, then return {@link #getSchemaString()} 2154 * 2155 * This method is only valid when the {@link #dbObjectType} is a schema object. 2156 * 2157 * @return schema name fetched from the SQLEnv 2158 */ 2159 public String getImplictSchemaString() { 2160 String implictSchema = null; 2161 if (this.implictSchemaName != null) return this.implictSchemaName; 2162 2163 if (schemaToken != null) return schemaToken.toString(); 2164 if (getSchemaString().length() > 0) return getSchemaString(); 2165 2166 if (sqlEnv == null) return null; 2167 2168 TSQLSchema s = searchImplicitSchema(); 2169 if (s != null){ 2170 implictSchema = s.getName(); 2171 } 2172 2173 return implictSchema; 2174 } 2175 2176 private String implictDatabaseName; 2177 private String implictSchemaName; 2178 2179 public void setImplictDatabaseName(String implictDatabaseName) { 2180 this.isImplicitDatabase = true; 2181 this.implictDatabaseName = implictDatabaseName; 2182 } 2183 2184 public void setImplictSchemaName(String implictSchemaName) { 2185 this.isImplicitSchema = true; 2186 this.implictSchemaName = implictSchemaName; 2187 } 2188 2189 public String getImplictDatabaseString() { 2190 String implictDatabase = null; 2191 if (implictDatabaseName != null) return implictDatabaseName; 2192 2193 if (getDatabaseString().length() > 0) return getDatabaseString(); 2194 2195 if (sqlEnv == null) return null; 2196 TSQLSchema s = searchImplicitSchema(); 2197 if (s != null){ 2198 TSQLCatalog c = s.getCatalog(); 2199 if (c != null){ 2200 implictDatabase = c.getName(); 2201 } 2202 } 2203 2204 return implictDatabase; 2205 } 2206 2207 protected TSQLSchema searchImplicitSchema(){ 2208 TSQLSchema s = null; 2209 if (sqlEnv == null) return null; 2210 switch (dbObjectType){ 2211 case table: 2212 case view: 2213 TSQLTable t = sqlEnv.searchTable(".."+this.getObjectString()); 2214 if (t != null){ 2215 s = t.getSchema(); 2216 } 2217 2218 break; 2219 case function: 2220 case procedure: 2221 TSQLFunction f = sqlEnv.searchFunction(".."+this.getObjectString()); 2222 if (f != null){ 2223 s = f.getSchema(); 2224 } 2225 break; 2226 default: 2227 break; 2228 } 2229 2230 return s; 2231 } 2232 2233 /** 2234 * The table name of this objectName, it's the same value as {@link #getObjectToken} if {@link #getDbObjectType} 2235 * is {@link gudusoft.gsqlparser.EDbObjectType#table} 2236 * 2237 * @return table name 2238 */ 2239 public TSourceToken getTableToken(){ 2240 if (objectToken == null) return null; 2241 else return objectToken; 2242 } 2243 2244 /** 2245 * String text of the table name 2246 * 2247 * @return string of the table name, return null if empty. 2248 */ 2249 public String getTableString(){ 2250 if (objectToken == null) return ""; 2251// else if (!((dbObjectType == EDbObjectType.table)||(dbObjectType == EDbObjectType.view))){ 2252// return ""; 2253// } 2254 else return objectToken.toString(); 2255 } 2256 2257 /** 2258 * String text of the object name 2259 * 2260 * @return string of the object name, return null if empty. 2261 */ 2262 public String getObjectString() { 2263 if (objectToken != null) 2264 return objectToken.getAstext(); 2265 else 2266 return "" ; 2267 } 2268 2269 /** 2270 * String text of the part name 2271 * 2272 * @return string of the part name, return null if empty. 2273 */ 2274 public String getPartString() { 2275 if (partToken != null) 2276 return partToken.getAstext(); 2277 else 2278 return "" ; 2279 } 2280 2281 // ===== Phase 5: Cached normalized getters (reduce repeated normalize() calls) ===== 2282 2283 private transient gudusoft.gsqlparser.sqlenv.IdentifierService identifierService; 2284 2285 /** 2286 * Lazy initialization of IdentifierService for normalized identifier caching 2287 */ 2288 private gudusoft.gsqlparser.sqlenv.IdentifierService getIdentifierService() { 2289 if (identifierService == null && sqlEnv != null) { 2290 gudusoft.gsqlparser.sqlenv.IdentifierProfile profile = gudusoft.gsqlparser.sqlenv.IdentifierProfile.forVendor( 2291 sqlEnv.getDBVendor(), 2292 gudusoft.gsqlparser.sqlenv.IdentifierProfile.VendorFlags.defaults() 2293 ); 2294 identifierService = new gudusoft.gsqlparser.sqlenv.IdentifierService(profile, null); 2295 } 2296 return identifierService; 2297 } 2298 2299 /** 2300 * Get normalized database string with caching (Phase 5 optimization). 2301 * 2302 * <p>This method caches the normalized database name to avoid repeated normalize() calls. 2303 * The cache is invalidated automatically when the IdentifierProfile changes (e.g., vendor switch). 2304 * 2305 * @return normalized database name, or empty string if not available 2306 */ 2307 public String getNormalizedDatabaseString() { 2308 if (sqlEnv == null) return getDatabaseString(); 2309 2310 try { 2311 gudusoft.gsqlparser.sqlenv.IdentifierService service = getIdentifierService(); 2312 if (service == null) return getDatabaseString(); 2313 2314 long currentFingerprint = service.getProfile().getFingerprint(); 2315 2316 // Check if cache is valid (not null and fingerprint matches) 2317 if (normalizedDatabase == null || cacheFingerprint != currentFingerprint) { 2318 String raw = getDatabaseString(); 2319 if (raw != null && !raw.isEmpty()) { 2320 normalizedDatabase = service.normalize(raw, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType.dotCatalog); 2321 } else { 2322 normalizedDatabase = ""; 2323 } 2324 cacheFingerprint = currentFingerprint; 2325 } 2326 2327 return normalizedDatabase; 2328 } catch (Throwable t) { 2329 // Fallback to non-cached on any error 2330 return getDatabaseString(); 2331 } 2332 } 2333 2334 /** 2335 * Get normalized schema string with caching (Phase 5 optimization). 2336 * 2337 * <p>This method caches the normalized schema name to avoid repeated normalize() calls. 2338 * The cache is invalidated automatically when the IdentifierProfile changes. 2339 * 2340 * @return normalized schema name, or empty string if not available 2341 */ 2342 public String getNormalizedSchemaString() { 2343 if (sqlEnv == null) return getSchemaString(); 2344 2345 try { 2346 gudusoft.gsqlparser.sqlenv.IdentifierService service = getIdentifierService(); 2347 if (service == null) return getSchemaString(); 2348 2349 long currentFingerprint = service.getProfile().getFingerprint(); 2350 2351 // Check if cache is valid 2352 if (normalizedSchema == null || cacheFingerprint != currentFingerprint) { 2353 String raw = getSchemaString(); 2354 if (raw != null && !raw.isEmpty()) { 2355 normalizedSchema = service.normalize(raw, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType.dotSchema); 2356 } else { 2357 normalizedSchema = ""; 2358 } 2359 cacheFingerprint = currentFingerprint; 2360 } 2361 2362 return normalizedSchema; 2363 } catch (Throwable t) { 2364 // Fallback to non-cached 2365 return getSchemaString(); 2366 } 2367 } 2368 2369 /** 2370 * Get normalized table string with caching (Phase 5 optimization). 2371 * 2372 * <p>This method caches the normalized table name to avoid repeated normalize() calls. 2373 * The cache is invalidated automatically when the IdentifierProfile changes. 2374 * 2375 * @return normalized table name, or empty string if not available 2376 */ 2377 public String getNormalizedTableString() { 2378 if (sqlEnv == null) return getTableString(); 2379 2380 try { 2381 gudusoft.gsqlparser.sqlenv.IdentifierService service = getIdentifierService(); 2382 if (service == null) return getTableString(); 2383 2384 long currentFingerprint = service.getProfile().getFingerprint(); 2385 2386 // Check if cache is valid 2387 if (normalizedTable == null || cacheFingerprint != currentFingerprint) { 2388 String raw = getTableString(); 2389 if (raw != null && !raw.isEmpty()) { 2390 normalizedTable = service.normalize(raw, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType.dotTable); 2391 } else { 2392 normalizedTable = ""; 2393 } 2394 cacheFingerprint = currentFingerprint; 2395 } 2396 2397 return normalizedTable; 2398 } catch (Throwable t) { 2399 // Fallback to non-cached 2400 return getTableString(); 2401 } 2402 } 2403 2404 /** 2405 * Get normalized column string with caching (Phase 5 optimization). 2406 * 2407 * <p>This method caches the normalized column name (from partToken) to avoid repeated normalize() calls. 2408 * The cache is invalidated automatically when the IdentifierProfile changes. 2409 * 2410 * @return normalized column name, or empty string if not available 2411 */ 2412 public String getNormalizedColumnString() { 2413 if (sqlEnv == null) return getPartString(); 2414 2415 try { 2416 gudusoft.gsqlparser.sqlenv.IdentifierService service = getIdentifierService(); 2417 if (service == null) return getPartString(); 2418 2419 long currentFingerprint = service.getProfile().getFingerprint(); 2420 2421 // Check if cache is valid 2422 if (normalizedColumn == null || cacheFingerprint != currentFingerprint) { 2423 String raw = getPartString(); 2424 if (raw != null && !raw.isEmpty()) { 2425 normalizedColumn = service.normalize(raw, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType.dotColumn); 2426 } else { 2427 normalizedColumn = ""; 2428 } 2429 cacheFingerprint = currentFingerprint; 2430 } 2431 2432 return normalizedColumn; 2433 } catch (Throwable t) { 2434 // Fallback to non-cached 2435 return getPartString(); 2436 } 2437 } 2438 2439 /** 2440 * Get normalized server string with caching (Phase 5 optimization). 2441 * 2442 * <p>This method caches the normalized server name to avoid repeated normalize() calls. 2443 * The cache is invalidated automatically when the IdentifierProfile changes. 2444 * 2445 * @return normalized server name, or empty string if not available 2446 */ 2447 public String getNormalizedServerString() { 2448 if (sqlEnv == null) return getServerString(); 2449 2450 try { 2451 gudusoft.gsqlparser.sqlenv.IdentifierService service = getIdentifierService(); 2452 if (service == null) return getServerString(); 2453 2454 long currentFingerprint = service.getProfile().getFingerprint(); 2455 2456 // Check if cache is valid 2457 if (normalizedServer == null || cacheFingerprint != currentFingerprint) { 2458 String raw = getServerString(); 2459 if (raw != null && !raw.isEmpty()) { 2460 // Use dotUnknown for server since there's no dotServer type 2461 normalizedServer = service.normalize(raw, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType.dotUnknown); 2462 } else { 2463 normalizedServer = ""; 2464 } 2465 cacheFingerprint = currentFingerprint; 2466 } 2467 2468 return normalizedServer; 2469 } catch (Throwable t) { 2470 // Fallback to non-cached 2471 return getServerString(); 2472 } 2473 } 2474 2475 2476 public void setExclamationmark(TSourceToken exclamationmark) { 2477 this.exclamationmark = exclamationmark; 2478 } 2479 2480 private TSourceToken exclamationmark; // objectToken@!, ! is dblink 2481 2482 private Boolean isParsed = false; 2483 2484 private void parseObjectName(){ 2485 parseTablename(); 2486 } 2487 2488 private void parseTablename(){ 2489 if ((this.dbObjectType == EDbObjectType.variable) ||(this.dbObjectType == EDbObjectType.stage))return; 2490 2491 switch (this.dbvendor){ 2492 case dbvteradata: 2493 case dbvhive: 2494 if (objectToken != null){ 2495 databaseToken = objectToken; 2496 //databaseToken.setDbObjType(TObjectName.ttobjDatabaseName); 2497 databaseToken.setDbObjectType(EDbObjectType.database); 2498 } 2499 objectToken = partToken; 2500 partToken = null; 2501 2502 break; 2503 default: 2504 if (databaseToken != null){ 2505 serverToken = databaseToken; 2506 //serverToken.setDbObjType(TObjectName.ttobjServerName); 2507 serverToken.setDbObjectType(EDbObjectType.server); 2508 } 2509 2510 if (schemaToken != null){ 2511 databaseToken = schemaToken; 2512 //databaseToken.setDbObjType(TObjectName.ttobjDatabaseName); 2513 databaseToken.setDbObjectType(EDbObjectType.database); 2514 } 2515 2516 if (objectToken != null){ 2517 schemaToken = objectToken; 2518 schemaToken.setDbObjType(TObjectName.ttobjSchemaName); 2519 } 2520 2521 objectToken = partToken; 2522 partToken = null; 2523 break; 2524 } 2525 2526 if (objectToken != null){ 2527 objectToken.setDbObjectType(this.dbObjectType); 2528 } 2529 } 2530 2531 private void parseVariableName(){ 2532 if (databaseToken != null){ 2533 serverToken = databaseToken; 2534 //serverToken.setDbObjType(TObjectName.ttobjServerName); 2535 serverToken.setDbObjectType(EDbObjectType.server); 2536 } 2537 2538 if (schemaToken != null){ 2539 databaseToken = schemaToken; 2540 // databaseToken.setDbObjType(TObjectName.ttobjDatabaseName); 2541 databaseToken.setDbObjectType(EDbObjectType.database); 2542 } 2543 2544 if (objectToken != null){ 2545 if (partToken != null){ 2546 schemaToken = objectToken; 2547 schemaToken.setDbObjType(TObjectName.ttobjSchemaName); 2548 objectToken = partToken; 2549 partToken = null; 2550 }else{ 2551 2552 } 2553 }else{ 2554 objectToken = partToken; 2555 partToken = null; 2556 } 2557 } 2558 2559 private String ansiSchemaName; 2560 private String ansiCatalogName; 2561 2562 2563 /** 2564 * In this SQL: select * from part1.part2, 2565 * In Hive, MySQL and Teradata, part1 will be treated as a database name, returned in getDatabaseString(), 2566 * while getSchemaString() return empty string. 2567 * 2568 * However, TObjectName.getAnsiSchemaName() will return part1, which means it's a schema name. 2569 * 2570 * If a table name is not qualified with a schema name, but GSP detect the schema for this table in the metadata 2571 * then, this method will return this detected schema name. 2572 * 2573 * @return schema name 2574 */ 2575 public String getAnsiSchemaName(){ 2576 String ret = this.getSchemaString(); 2577 if ((ret.length() == 0) && ((this.getImplictSchemaString() != null) && (!this.getImplictSchemaString().equalsIgnoreCase("default")))){ 2578 ret = this.getImplictSchemaString(); 2579 } 2580 2581 switch (dbvendor){ 2582 case dbvmysql: 2583 case dbvhive: 2584 case dbvteradata: 2585 case dbvimpala: 2586 ret = this.getDatabaseString(); 2587 break; 2588 } 2589 return ret; 2590 } 2591 2592 /** 2593 * If a table name is not qualified with a database name, but GSP detect the database for this table in the metadata 2594 * then, this method will return this detected database name. 2595 * 2596 * @return 2597 */ 2598 public String getAnsiCatalogName(){ 2599 String ret = this.getDatabaseString(); 2600 if (( ret.length() == 0) && (this.getImplictDatabaseString() != null) && (!this.getImplictDatabaseString().equalsIgnoreCase("default"))){ 2601 ret = this.getImplictDatabaseString(); 2602 } 2603 2604 switch (dbvendor){ 2605 case dbvmysql: 2606 case dbvhive: 2607 case dbvteradata: 2608 case dbvimpala: 2609 ret = ""; 2610 break; 2611 } 2612 2613 return ret; 2614 } 2615 2616 private void parseFunctionName(){ 2617 this.parseTablename(); 2618 } 2619 2620 private void parseColumnMethodName(){ 2621 // objectType = ttobjColumnMethod; 2622 2623 methodToken = objectToken; 2624 partToken = schemaToken; 2625 2626 objectToken = null;//; 2627 schemaToken = null; 2628 } 2629 2630 private void parseColumnName(){ 2631 assert(partToken != null); 2632 } 2633 2634 public TObjectName(){ 2635 } 2636 2637 /** 2638 * List the number of parts made up this objectName 2639 * 2640 * @return the number of parts that made up this objectName 2641 */ 2642 public int getNumberOfPart() { 2643 return numberOfPart; 2644 } 2645 2646 private int numberOfPart = 1; 2647 2648 public static TObjectName createObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType){ 2649 return new TObjectName(dbVendor,dbObjectType); 2650 } 2651 2652 2653 public static TObjectName createObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token1){ 2654 return new TObjectName(dbVendor,dbObjectType,token1); 2655 } 2656 2657 public static TObjectName createObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType, String str) { 2658 String[] parts = str.split("\\."); 2659 if (parts.length == 1) { 2660 return new TObjectName(dbVendor, dbObjectType, new TSourceToken(parts[0])); 2661 } else if (parts.length == 2) { 2662 return new TObjectName(dbVendor, dbObjectType, new TSourceToken(parts[0]), new TSourceToken(parts[1])); 2663 } else if (parts.length == 3) { 2664 return new TObjectName(dbVendor, dbObjectType, new TSourceToken(parts[0]), new TSourceToken(parts[1]), new TSourceToken(parts[2])); 2665 } else if (parts.length == 4) { 2666 return new TObjectName(dbVendor, dbObjectType, new TSourceToken(parts[0]), new TSourceToken(parts[1]), new TSourceToken(parts[2]), new TSourceToken(parts[3])); 2667 } 2668 return new TObjectName(dbVendor, dbObjectType, new TSourceToken(str)); 2669 } 2670 2671 2672 public static TObjectName createObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token1,TSourceToken token2){ 2673 return new TObjectName(dbVendor,dbObjectType,token1,token2); 2674 } 2675 2676 public static TObjectName createObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token1,TSourceToken token2,TSourceToken token3){ 2677 return new TObjectName(dbVendor,dbObjectType,token1,token2,token3); 2678 } 2679 2680 public static TObjectName createObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token1,TSourceToken token2,TSourceToken token3,TSourceToken token4){ 2681 return new TObjectName(dbVendor,dbObjectType,token1,token2,token3,token4); 2682 } 2683 2684 /** 2685 * @deprecated As of v2.0.7.1, please use {@link #TObjectName(EDbObjectType, TSourceToken)} instead. 2686 * 2687 * Class constructor specifying object name and object type. 2688 * <p> 2689 * Use {@link gudusoft.gsqlparser.TGSqlParser#parseObjectName} to create an objectName more than 2 parts. 2690 * 2691 * @param token name of this object 2692 * @param dbObjectType type of this object 2693 */ 2694 private TObjectName(TSourceToken token,EDbObjectType dbObjectType){ 2695 this(dbObjectType,token); 2696 } 2697 2698 public void splitNameInQuotedIdentifier(){ 2699 if (this.dbvendor != EDbVendor.dbvbigquery && this.dbvendor != EDbVendor.dbvsnowflake) return; 2700 if (this.objectToken == null) return; 2701 TSourceToken token = this.objectToken; 2702 2703 // For Snowflake IDENTIFIER function, the token is a single-quoted string literal 2704 // which has quoteType notQuoted but starts with single quote 2705 boolean isSnowflakeSingleQuotedString = (this.dbvendor == EDbVendor.dbvsnowflake) 2706 && token.toString().startsWith("'"); 2707 2708 if (getQuoteType() == EQuoteType.notQuoted && !isSnowflakeSingleQuotedString) return; 2709// if ((this.dbvendor != EDbVendor.dbvbigquery) 2710// &&(getQuoteType() == EQuoteType.doubleQuote)) return; 2711 2712 String tokenStr = token.toString(); 2713 char outerQuoteChar = tokenStr.charAt(0); 2714 String s = TBaseType.getTextWithoutQuoted(tokenStr); 2715 String[] a = s.split("[.]"); 2716 if (a.length == 1){ 2717 // this.objectToken = token; 2718 }else if (a.length == 2){ 2719 String objPart = a[1]; 2720 String schemaPart = a[0]; 2721 2722 // For Snowflake IDENTIFIER function with single-quoted string containing double-quoted parts 2723 // e.g., IDENTIFIER('"SCHEMA"."TABLE"') -> parts already have double quotes 2724 // For unquoted parts, e.g., IDENTIFIER('schema.table') -> use parts as-is 2725 boolean isSnowflakeIdentifierFunction = (this.dbvendor == EDbVendor.dbvsnowflake) && (outerQuoteChar == '\''); 2726 2727 if (isSnowflakeIdentifierFunction) { 2728 this.objectToken = new TSourceToken(objPart); 2729 this.schemaToken = new TSourceToken(schemaPart); 2730 } else { 2731 this.objectToken = new TSourceToken(outerQuoteChar + objPart + outerQuoteChar); 2732 this.schemaToken = new TSourceToken(outerQuoteChar + schemaPart + outerQuoteChar); 2733 } 2734 }else if (a.length == 3){ 2735 String objPart = a[2]; 2736 String schemaPart = a[1]; 2737 String dbPart = a[0]; 2738 2739 boolean isSnowflakeIdentifierFunction = (this.dbvendor == EDbVendor.dbvsnowflake) && (outerQuoteChar == '\''); 2740 2741 if (isSnowflakeIdentifierFunction) { 2742 this.objectToken = new TSourceToken(objPart); 2743 this.schemaToken = new TSourceToken(schemaPart); 2744 this.databaseToken = new TSourceToken(dbPart); 2745 } else { 2746 this.objectToken = new TSourceToken(outerQuoteChar + objPart + outerQuoteChar); 2747 this.schemaToken = new TSourceToken(outerQuoteChar + schemaPart + outerQuoteChar); 2748 this.databaseToken = new TSourceToken(outerQuoteChar + dbPart + outerQuoteChar); 2749 } 2750 } 2751 2752 } 2753 2754 private TObjectName(EDbVendor dbVendor){ 2755 this.dbvendor = dbVendor; 2756 } 2757 2758 private TObjectName(EDbVendor dbVendor,EDbObjectType dbObjectType){ 2759 this.dbvendor = dbVendor; 2760 this.dbObjectType = dbObjectType; 2761 } 2762 2763 private TObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token){ 2764 this.dbvendor = dbVendor; 2765 numberOfPart = 1; 2766 this.setStartToken(token); 2767 this.setEndToken(token); 2768 2769 this.dbObjectType = dbObjectType; 2770 switch (dbObjectType){ 2771 case column: 2772 this.partToken = token; 2773 break; 2774 case method: 2775 this.methodToken = token; 2776 break; 2777 case table: 2778 case function: 2779 case procedure: 2780 case materializedView: 2781 case alias: 2782 case module: 2783 case sequence: 2784 case collation: 2785 this.objectToken = token; 2786 splitNameInQuotedIdentifier(); 2787 break; 2788 default: 2789 this.objectToken = token; 2790 break; 2791 } 2792 } 2793 2794 private void initWithOneToken(EDbObjectType dbObjectType,TSourceToken token){ 2795 numberOfPart = 1; 2796 this.setStartToken(token); 2797 this.setEndToken(token); 2798 2799 this.dbObjectType = dbObjectType; 2800 switch (dbObjectType){ 2801 case column: 2802 this.partToken = token; 2803 break; 2804 case method: 2805 this.methodToken = token; 2806 break; 2807 case table: 2808 case function: 2809 case procedure: 2810 case materializedView: 2811 case alias: 2812 case module: 2813 case sequence: 2814 case collation: 2815 case stage: 2816 this.objectToken = token; 2817 splitNameInQuotedIdentifier(); 2818 break; 2819 case namespace: 2820 this.schemaToken = token; 2821 break; 2822 default: 2823 this.objectToken = token; 2824 break; 2825 } 2826 } 2827 2828 private void initWithTwoTokens(EDbObjectType dbObjectType,TSourceToken token2,TSourceToken token1){ 2829 initWithOneToken(dbObjectType,token1); 2830 numberOfPart = 2; 2831 this.setStartToken(token2); 2832 this.setEndToken(token1); 2833 2834 switch (dbObjectType){ 2835 case column: 2836 this.objectToken = token2; 2837 break; 2838 case method: 2839 this.partToken = token2; 2840 break; 2841 case table: 2842 case function: 2843 case procedure: 2844 case materializedView: 2845 case alias: 2846 case module: 2847 case sequence: 2848 case collation: 2849 case stage: 2850 this.schemaToken = token2; 2851 break; 2852 case namespace: 2853 this.databaseToken = token2; 2854 break; 2855 default: 2856 this.schemaToken = token2; 2857 break; 2858 } 2859 2860 } 2861 2862 private void initWithThreeTokens(EDbObjectType dbObjectType,TSourceToken token3,TSourceToken token2,TSourceToken token1){ 2863 initWithTwoTokens(dbObjectType,token2,token1); 2864 numberOfPart = 3; 2865 this.setStartToken(token3); 2866 this.setEndToken(token1); 2867 2868 switch (dbObjectType){ 2869 case column: 2870 this.schemaToken = token3; 2871 break; 2872 case method: 2873 this.objectToken = token3; 2874 break; 2875 case table: 2876 case function: 2877 case procedure: 2878 case materializedView: 2879 case alias: 2880 case module: 2881 case sequence: 2882 case collation: 2883 case stage: 2884 this.databaseToken = token3; 2885 break; 2886 default: 2887 this.databaseToken = token3; 2888 break; 2889 } 2890 2891 } 2892 2893 private void initWithFourTokens(EDbObjectType dbObjectType,TSourceToken token4,TSourceToken token3,TSourceToken token2,TSourceToken token1){ 2894 initWithThreeTokens(dbObjectType,token3,token2,token1); 2895 numberOfPart = 4; 2896 this.setStartToken(token4); 2897 this.setEndToken(token1); 2898 2899 switch (dbObjectType){ 2900 case column: 2901 this.databaseToken = token4; 2902 break; 2903 case method: 2904 this.schemaToken = token4; 2905 break; 2906 case table: 2907 case function: 2908 case procedure: 2909 case materializedView: 2910 case alias: 2911 case module: 2912 case sequence: 2913 case collation: 2914 this.serverToken = token4; 2915 break; 2916 default: 2917 this.serverToken = token4; 2918 break; 2919 } 2920 2921 } 2922 2923 /** 2924 * @deprecated As of v2.0.7.1, please use {@link TObjectName#createObjectName(EDbVendor, EDbObjectType, TSourceToken)} instead. 2925 * 2926 * @param dbObjectType 2927 * @param token 2928 */ 2929 private TObjectName(EDbObjectType dbObjectType,TSourceToken token){ 2930 initWithOneToken(dbObjectType,token); 2931 } 2932 2933 private TObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token2,TSourceToken token1){ 2934 this(dbVendor,dbObjectType,token1); 2935 numberOfPart = 2; 2936 this.setStartToken(token2); 2937 this.setEndToken(token1); 2938 2939 switch (dbObjectType){ 2940 case column: 2941 this.objectToken = token2; 2942 break; 2943 case method: 2944 this.partToken = token2; 2945 break; 2946 case table: 2947 case function: 2948 case procedure: 2949 case materializedView: 2950 case alias: 2951 case module: 2952 case sequence: 2953 case collation: 2954 if (dbVendor == EDbVendor.dbvteradata){ 2955 this.databaseToken = token2; 2956 }else{ 2957 this.schemaToken = token2; 2958 } 2959 2960 break; 2961 default: 2962 this.schemaToken = token2; 2963 break; 2964 } 2965 2966 } 2967 2968 2969 /** 2970 * @deprecated since ver 2.5.9.8 2971 * 2972 * @param dbObjectType 2973 * @param token2 2974 * @param token1 2975 */ 2976 private TObjectName(EDbObjectType dbObjectType,TSourceToken token2,TSourceToken token1){ 2977 this(dbObjectType,token1); 2978 numberOfPart = 2; 2979 this.setStartToken(token2); 2980 this.setEndToken(token1); 2981 2982 switch (dbObjectType){ 2983 case column: 2984 this.objectToken = token2; 2985 break; 2986 case method: 2987 this.partToken = token2; 2988 break; 2989 case table: 2990 case function: 2991 case procedure: 2992 case materializedView: 2993 case alias: 2994 case module: 2995 case sequence: 2996 case collation: 2997 this.schemaToken = token2; 2998 break; 2999 default: 3000 this.schemaToken = token2; 3001 break; 3002 } 3003 } 3004 private TObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token3,TSourceToken token2,TSourceToken token1){ 3005 this(dbVendor,dbObjectType,token2,token1); 3006 numberOfPart = 3; 3007 this.setStartToken(token3); 3008 this.setEndToken(token1); 3009 3010 switch (dbObjectType){ 3011 case column: 3012 this.schemaToken = token3; 3013 break; 3014 case method: 3015 this.objectToken = token3; 3016 break; 3017 case table: 3018 case function: 3019 case procedure: 3020 case materializedView: 3021 case alias: 3022 case module: 3023 case sequence: 3024 case collation: 3025 this.databaseToken = token3; 3026 break; 3027 default: 3028 this.databaseToken = token3; 3029 break; 3030 } 3031 3032 } 3033 3034 3035 /** 3036 * @deprecated since ver 2.5.9.8 3037 * 3038 * @param dbObjectType 3039 * @param token3 3040 * @param token2 3041 * @param token1 3042 */ 3043 private TObjectName(EDbObjectType dbObjectType,TSourceToken token3,TSourceToken token2,TSourceToken token1){ 3044 this(dbObjectType,token2,token1); 3045 numberOfPart = 3; 3046 this.setStartToken(token3); 3047 this.setEndToken(token1); 3048 3049 switch (dbObjectType){ 3050 case column: 3051 this.schemaToken = token3; 3052 break; 3053 case method: 3054 this.objectToken = token3; 3055 break; 3056 case table: 3057 case function: 3058 case procedure: 3059 case materializedView: 3060 case alias: 3061 case module: 3062 case sequence: 3063 case collation: 3064 this.databaseToken = token3; 3065 break; 3066 default: 3067 this.databaseToken = token3; 3068 break; 3069 } 3070 } 3071 3072 private TObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token4,TSourceToken token3,TSourceToken token2,TSourceToken token1){ 3073 this(dbVendor,dbObjectType,token3,token2,token1); 3074 numberOfPart = 4; 3075 this.setStartToken(token4); 3076 this.setEndToken(token1); 3077 3078 switch (dbObjectType){ 3079 case column: 3080 this.databaseToken = token4; 3081 break; 3082 case method: 3083 this.schemaToken = token4; 3084 break; 3085 case table: 3086 case function: 3087 case procedure: 3088 case materializedView: 3089 case alias: 3090 case module: 3091 case sequence: 3092 case collation: 3093 this.serverToken = token4; 3094 break; 3095 default: 3096 this.serverToken = token4; 3097 break; 3098 } 3099 3100 } 3101 3102 /** 3103 * @deprecated since ver 2.5.9.8 3104 * 3105 * @param dbObjectType 3106 * @param token4 3107 * @param token3 3108 * @param token2 3109 * @param token1 3110 */ 3111 private TObjectName(EDbObjectType dbObjectType,TSourceToken token4,TSourceToken token3,TSourceToken token2,TSourceToken token1){ 3112 this(dbObjectType,token3,token2,token1); 3113 numberOfPart = 4; 3114 this.setStartToken(token4); 3115 this.setEndToken(token1); 3116 3117 switch (dbObjectType){ 3118 case column: 3119 this.databaseToken = token4; 3120 break; 3121 case method: 3122 this.schemaToken = token4; 3123 break; 3124 case table: 3125 case function: 3126 case procedure: 3127 case materializedView: 3128 case alias: 3129 case module: 3130 case sequence: 3131 case collation: 3132 this.serverToken = token4; 3133 break; 3134 default: 3135 this.serverToken = token4; 3136 break; 3137 } 3138 } 3139 3140 /** 3141 * @deprecated As of v2.0.7.1, please use {@link #TObjectName(EDbObjectType, TSourceToken, TSourceToken)} instead. 3142 * 3143 * Class constructor specifying object, part name and object type. 3144 * Use this constructor to create a <code>table.column</code> objectName. 3145 * Use {@link gudusoft.gsqlparser.TGSqlParser#parseObjectName} to create an objectName more than 2 parts. 3146 * 3147 * @param pObjectToken name of this object, usually it's the table name 3148 * @param pPartToken name of the column 3149 * @param dbObjectType type of this object, usually it's {@link gudusoft.gsqlparser.EDbObjectType#column} 3150 */ 3151 private TObjectName(TSourceToken pObjectToken,TSourceToken pPartToken,EDbObjectType dbObjectType){ 3152 this(dbObjectType,pObjectToken,pPartToken); 3153 } 3154 3155 public void init(Object arg1) 3156 { 3157 partToken = (TSourceToken)arg1; 3158 numberOfPart = 1; 3159 this.setStartToken(partToken); 3160 this.setEndToken(partToken); 3161 } 3162 3163 public void init(Object arg1, Object arg2) 3164 { 3165 if (arg1 instanceof EDbObjectType){ 3166 initWithOneToken((EDbObjectType)arg1,(TSourceToken) arg2); 3167 3168 }else{ 3169 numberOfPart = 0; 3170 objectToken = (TSourceToken)arg1; 3171 partToken = (TSourceToken)arg2; 3172 if (partToken != null) numberOfPart++; 3173 if (objectToken != null) numberOfPart++; 3174 3175 3176 if(objectToken != null){ 3177 this.setStartToken(objectToken); 3178 }else{ 3179 this.setStartToken(partToken); 3180 } 3181 3182 if (partToken != null){ 3183 this.setEndToken(partToken); 3184 }else{ 3185 this.setEndToken(objectToken); 3186 } 3187 } 3188 } 3189 3190 public void init(EDbObjectType dbObjectType, Object arg1, Object arg2, Object arg3){ 3191 numberOfPart = 0; 3192 if (arg1 != null) numberOfPart++; 3193 if (arg2 != null) numberOfPart++; 3194 if (arg3 != null) numberOfPart++; 3195 3196 this.dbObjectType = dbObjectType; 3197 this.setStartToken((TSourceToken)arg1); 3198 this.setEndToken((TSourceToken)arg3); 3199 switch (this.dbObjectType){ 3200 case column: 3201 schemaToken = (TSourceToken)arg1; 3202 objectToken = (TSourceToken)arg2; 3203 partToken = (TSourceToken)arg3; 3204 break; 3205 case table: 3206 case function: 3207 case procedure: 3208 case materializedView: 3209 case module: 3210 case sequence: 3211 databaseToken = (TSourceToken) arg1; 3212 schemaToken = (TSourceToken)arg2; 3213 objectToken = (TSourceToken)arg3; 3214 break; 3215 case alias: 3216 break; 3217 default: 3218 break; 3219 } 3220 3221 } 3222 3223 public void init(Object arg1, Object arg2, Object arg3) 3224 { 3225 if (arg1 instanceof EDbObjectType){ 3226 initWithTwoTokens((EDbObjectType)arg1,(TSourceToken) arg2,(TSourceToken) arg3); 3227 }else{ 3228 numberOfPart = 0; 3229 if (arg1 != null) numberOfPart++; 3230 if (arg2 != null) numberOfPart++; 3231 if (arg3 != null) numberOfPart++; 3232 3233 if (dbvendor == EDbVendor.dbvteradata){ 3234 databaseToken = (TSourceToken) arg1; 3235 this.setStartToken(databaseToken); 3236 }else{ 3237 schemaToken = (TSourceToken)arg1; 3238 this.setStartToken(schemaToken); 3239 if (schemaToken != null) 3240 {schemaToken.setDbObjType(TObjectName.ttobjSchemaName);} 3241 } 3242 3243 objectToken = (TSourceToken)arg2; 3244 partToken = (TSourceToken)arg3; 3245 this.setEndToken(partToken); 3246 } 3247 } 3248 3249 public void init(Object arg1, Object arg2, Object arg3, Object arg4) 3250 { 3251 if (arg1 instanceof EDbObjectType){ 3252 //this.dbObjectType = (EDbObjectType)arg1; 3253 //init(arg2,arg3,arg4); 3254 initWithThreeTokens((EDbObjectType)arg1,(TSourceToken)arg2,(TSourceToken)arg3,(TSourceToken)arg4); 3255 }else{ 3256 numberOfPart = 0; 3257 if (arg1 != null) numberOfPart++; 3258 if (arg2 != null) numberOfPart++; 3259 if (arg3 != null) numberOfPart++; 3260 if (arg4 != null) numberOfPart++; 3261 3262 //serverToken = (TSourceToken)arg1; 3263 databaseToken = (TSourceToken)arg1; 3264 schemaToken = (TSourceToken)arg2; 3265 objectToken = (TSourceToken)arg3; 3266 partToken = (TSourceToken)arg4; 3267 this.setStartToken(databaseToken); 3268 this.setEndToken(partToken); 3269 if (databaseToken != null){ 3270 //databaseToken.setDbObjType(TObjectName.ttobjDatabaseName); 3271 databaseToken.setDbObjectType(EDbObjectType.database); 3272 }else { 3273 } 3274 if (schemaToken != null){ 3275 schemaToken.setDbObjType(TObjectName.ttobjSchemaName); 3276 }else { 3277 } 3278 } 3279 } 3280 3281 public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) 3282 { 3283 numberOfPart = 0; 3284 if (arg1 != null) numberOfPart++; 3285 if (arg2 != null) numberOfPart++; 3286 if (arg3 != null) numberOfPart++; 3287 if (arg4 != null) numberOfPart++; 3288 if (arg5 != null) numberOfPart++; 3289 3290 serverToken = (TSourceToken)arg1; 3291 databaseToken = (TSourceToken)arg2; 3292 schemaToken = (TSourceToken)arg3; 3293 objectToken = (TSourceToken)arg4; 3294 partToken = (TSourceToken)arg5; 3295 3296 this.setStartToken(serverToken); 3297 this.setEndToken(partToken); 3298 3299 if (serverToken != null){ 3300 //serverToken.setDbObjType(TObjectName.ttobjServerName); 3301 serverToken.setDbObjectType(EDbObjectType.server); 3302 }else{ 3303 } 3304 if (databaseToken != null){ 3305 //databaseToken.setDbObjType(TObjectName.ttobjDatabaseName); 3306 databaseToken.setDbObjectType(EDbObjectType.database); 3307 }else{ 3308 } 3309 if (schemaToken != null){ 3310 schemaToken.setDbObjType(TObjectName.ttobjSchemaName); 3311 }else{ 3312 } 3313 } 3314 3315 public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) 3316 { 3317 numberOfPart = 0; 3318 if (arg1 != null) numberOfPart++; 3319 if (arg2 != null) numberOfPart++; 3320 if (arg3 != null) numberOfPart++; 3321 if (arg4 != null) numberOfPart++; 3322 if (arg5 != null) numberOfPart++; 3323 if (arg6 != null) numberOfPart++; 3324 3325 serverToken = (TSourceToken)arg1; 3326 databaseToken = (TSourceToken)arg2; 3327 schemaToken = (TSourceToken)arg3; 3328 objectToken = (TSourceToken)arg4; 3329 partToken = (TSourceToken)arg5; 3330 propertyToken = (TSourceToken)arg6; 3331 3332 this.setStartToken(serverToken); 3333 this.setEndToken(propertyToken); 3334 3335 if (serverToken != null){ 3336 //serverToken.setDbObjType(TObjectName.ttobjServerName); 3337 serverToken.setDbObjectType(EDbObjectType.server); 3338 }else{ 3339 } 3340 if (databaseToken != null){ 3341 // databaseToken.setDbObjType(TObjectName.ttobjDatabaseName); 3342 databaseToken.setDbObjectType(EDbObjectType.database); 3343 }else{ 3344 } 3345 if (schemaToken != null){ 3346 schemaToken.setDbObjType(TObjectName.ttobjSchemaName); 3347 }else{ 3348 } 3349 } 3350 3351 /** 3352 * Init with 7 tokens for deeply nested struct field access (e.g., BigQuery) 3353 * Pattern: a.b.c.d.e.f.g (7 parts) 3354 */ 3355 public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7) 3356 { 3357 // First 6 parts use standard tokens 3358 init(arg1, arg2, arg3, arg4, arg5, arg6); 3359 3360 // 7th part goes to additionalParts 3361 if (arg7 != null) { 3362 if (additionalParts == null) { 3363 additionalParts = new java.util.ArrayList<>(); 3364 } 3365 additionalParts.add((TSourceToken) arg7); 3366 numberOfPart++; 3367 this.setEndToken((TSourceToken) arg7); 3368 } 3369 } 3370 3371 /** 3372 * Init with 8 tokens for deeply nested struct field access 3373 * Pattern: a.b.c.d.e.f.g.h (8 parts) 3374 */ 3375 public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8) 3376 { 3377 init(arg1, arg2, arg3, arg4, arg5, arg6, arg7); 3378 3379 if (arg8 != null) { 3380 if (additionalParts == null) { 3381 additionalParts = new java.util.ArrayList<>(); 3382 } 3383 additionalParts.add((TSourceToken) arg8); 3384 numberOfPart++; 3385 this.setEndToken((TSourceToken) arg8); 3386 } 3387 } 3388 3389 /** 3390 * Init with 9 tokens for deeply nested struct field access 3391 * Pattern: a.b.c.d.e.f.g.h.i (9 parts) 3392 */ 3393 public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9) 3394 { 3395 init(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); 3396 3397 if (arg9 != null) { 3398 if (additionalParts == null) { 3399 additionalParts = new java.util.ArrayList<>(); 3400 } 3401 additionalParts.add((TSourceToken) arg9); 3402 numberOfPart++; 3403 this.setEndToken((TSourceToken) arg9); 3404 } 3405 } 3406 3407 /** 3408 * Init with 10 tokens for deeply nested struct field access 3409 * Pattern: a.b.c.d.e.f.g.h.i.j (10 parts) 3410 */ 3411 public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9, Object arg10) 3412 { 3413 init(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); 3414 3415 if (arg10 != null) { 3416 if (additionalParts == null) { 3417 additionalParts = new java.util.ArrayList<>(); 3418 } 3419 additionalParts.add((TSourceToken) arg10); 3420 numberOfPart++; 3421 this.setEndToken((TSourceToken) arg10); 3422 } 3423 } 3424 3425 /** 3426 * Get additional parts beyond the standard 6 tokens. 3427 * Used for deeply nested struct field access in databases like BigQuery. 3428 * 3429 * @return list of additional source tokens, or null if none 3430 */ 3431 public java.util.List<TSourceToken> getAdditionalParts() { 3432 return additionalParts; 3433 } 3434 3435 /** 3436 * The column position of this objectName in the SQL 3437 * 3438 * @return column position 3439 */ 3440 @Override 3441 public long getColumnNo() { 3442 long retval = -1; 3443 if (partToken != null) { retval = partToken.columnNo;} 3444 if (objectToken != null) { 3445 retval = objectToken.columnNo; 3446 } 3447 if (schemaToken != null) { 3448 retval = schemaToken.columnNo; 3449 } 3450 if (databaseToken != null) { 3451 retval = databaseToken.columnNo; 3452 } 3453 if (serverToken != null) { 3454 retval = serverToken.columnNo; 3455 } 3456 return retval; 3457 } 3458 3459 /** 3460 * The line number of this objectName in SQL 3461 * 3462 * @return the line number 3463 */ 3464 @Override 3465 public long getLineNo() { 3466 long retval = -1; 3467 if (partToken != null) { retval = partToken.lineNo;} 3468 if (objectToken != null) { 3469 retval = objectToken.lineNo; 3470 } 3471 if (schemaToken != null) { 3472 retval = schemaToken.lineNo; 3473 } 3474 if (databaseToken != null) { 3475 retval = databaseToken.lineNo; 3476 } 3477 if (serverToken != null) { 3478 retval = serverToken.lineNo; 3479 } 3480 return retval; 3481 } 3482 3483 private TObjectNameList referencedObjects = null; 3484 3485 public TObjectNameList getReferencedObjects() { 3486 if (referencedObjects == null){ 3487 referencedObjects = new TObjectNameList(); 3488 } 3489 return referencedObjects; 3490 } 3491 3492 /** 3493 * Returns only the column name if it's prefixed with a table name 3494 * 3495 * @return only the column name if it's prefixed with a table name 3496 */ 3497 public String getColumnNameOnly(){ 3498 3499 if (getPartToken() == null) return ""; 3500 else return getPartToken().toString(); 3501 } 3502 3503 public void accept(TParseTreeVisitor v){ 3504 v.preVisit(this); 3505 v.postVisit(this); 3506 } 3507 3508 public void acceptChildren(TParseTreeVisitor v){ 3509 v.preVisit(this); 3510 v.postVisit(this); 3511 } 3512 3513// private TSourceToken sortType = null; 3514// 3515// public void setSortType(TSourceToken sortType) { 3516// this.sortType = sortType; 3517// } 3518// 3519// /** 3520// * When this object is column in primary key(column,...), unique key(column,...) in sql server 3521// * there maybe sort information like column asc, column desc 3522// * this token represents for ASC, DESC if specified. 3523// * 3524// * @return ASC, DESC or null 3525// */ 3526// 3527// public TSourceToken getSortType() { 3528// return sortType; 3529// } 3530 3531 /** 3532 * It's the same as {@link #getPartToken} if {@link #getDbObjectType} is {@link gudusoft.gsqlparser.EDbObjectType#column} 3533 * 3534 * @return source token that represents column, return null if this objectName is not type of column 3535 * 3536 */ 3537 public TSourceToken getColumnToken(){ 3538 TSourceToken ret = null; 3539 if (this.getObjectType() == ttobjColumn){ 3540 ret = this.getPartToken(); 3541 } 3542 return ret; 3543 } 3544 3545 /* 3546 * re-arranage objectname to make it a valid name includes attribute name 3547 * used by teradata yacc file only. 3548 * valid syntax: 3549 * column.attr1().attr2(). 3550 * column.attr1().attr2().attr3() 3551 * table.column.attr1().attr2(). 3552 * table.column.attr1().attr2().attr3() 3553 * 3554 * @return 3555 */ 3556 public boolean isAttributeNameInObjectName(TSourceToken leftparen,TSourceToken rightparen){ 3557 boolean ret = false; 3558 if ((this.partToken == null) || (this.objectToken == null)){ 3559 return ret; 3560 } 3561 this.objectType = TObjectName.ttobjColumn; 3562 this.columnAttributes = new TObjectNameList(); 3563 TObjectName attr1 = new TObjectName(); 3564 attr1.objectType = TObjectName.ttobjAttribute; 3565 attr1.init(this.partToken); 3566 attr1.setEndToken(rightparen); 3567 this.columnAttributes.addObjectName(attr1); 3568 3569 this.partToken = this.objectToken; 3570 3571 if (this.schemaToken != null){ 3572 this.objectToken = this.schemaToken; 3573 } 3574 3575 return true; 3576 } 3577 3578 /** 3579 * Used internally in hive .y file to merge two objectNames 3580 */ 3581 public void mergeObjectName(TObjectName objectName){ 3582 this.objectToken = this.partToken; 3583 this.partToken = objectName.getPartToken(); 3584 this.setStartToken(objectToken); 3585 this.setEndToken(partToken); 3586 } 3587 3588 public void mergeObjectName(TObjectName objectName,TObjectName objectName2){ 3589 this.schemaToken = this.partToken; 3590 this.objectToken = objectName.getPartToken(); 3591 this.partToken = objectName2.getPartToken(); 3592 this.setStartToken(schemaToken); 3593 this.setEndToken(partToken); 3594 } 3595 3596 3597 public void columnToProperty(){ 3598 // if (numberOfPart == 1) return; 3599 if (this.pseudoTableType != EPseudoTableType.none) return; // pseudo table tokens already in correct position 3600 if (this.propertyToken != null) return; // columnToProperty() already called 3601 if (! ((this.partToken != null) && (this.objectToken != null))) return; // 既然是 column.property , 那么 partToken and objectToken 不能为空 3602 3603 this.propertyToken = this.partToken; 3604 this.partToken = this.objectToken; 3605 this.objectToken = this.schemaToken; 3606 3607 this.setDbObjectTypeDirectly(EDbObjectType.column); 3608 } 3609 3610 public void appendObjectName(TObjectName objectName){ 3611 if (this.databaseToken != null){ 3612 this.serverToken = this.databaseToken; 3613 } 3614 if (this.schemaToken != null){ 3615 this.databaseToken = this.schemaToken; 3616 } 3617 if (this.objectToken != null){ 3618 this.schemaToken = this.objectToken; 3619 } 3620 this.objectToken = this.partToken; 3621 this.partToken = objectName.getPartToken(); 3622 this.setEndTokenDirectly(this.partToken); 3623 } 3624 3625 private TSourceToken commentString; 3626 3627 public void setCommentString(TSourceToken commentString) { 3628 this.commentString = commentString; 3629 } 3630 3631 public TSourceToken getCommentString() { 3632 3633 return commentString; 3634 } 3635 3636 3637 /** 3638 * The X and Y position of this objectName in the SQL 3639 * 3640 * @return coordinate in string text 3641 */ 3642 public String coordinate(){ 3643 return this.getStartToken().lineNo+","+this.getEndToken().columnNo; 3644 } 3645 3646 3647 /** 3648 * @deprecated replaced by {@link EDbObjectType}. 3649 * 3650 * this is not an object, like sysdate function in oracle database 3651 */ 3652 public final static int ttobjNotAObject = -1; 3653 3654 /** 3655 * @deprecated replaced by {@link EDbObjectType}. 3656 * object type can't be determined. 3657 */ 3658 public final static int ttobjUnknown = 0; 3659 3660 /** 3661 * @deprecated replaced by {@link EDbObjectType}. 3662 * column in table, objectToken is table if specified, and partToken is column name. 3663 */ 3664 public final static int ttobjColumn = 1; 3665 3666 /** 3667 * @deprecated replaced by {@link EDbObjectType}. 3668 * column alias in objectToken. 3669 */ 3670 public final static int ttobjColumnAlias = 2; 3671 3672 /** 3673 * @deprecated replaced by {@link EDbObjectType}. 3674 * table name in objectToken. 3675 */ 3676 public final static int ttobjTable = 3; 3677 3678 3679 /** 3680 * @deprecated replaced by {@link EDbObjectType}. 3681 * parameter name in objectToken. 3682 */ 3683 public final static int ttobjParameter = 9; 3684 3685 /** 3686 * @deprecated replaced by {@link EDbObjectType}. 3687 * variable name in objectToken. 3688 */ 3689 public final static int ttobjVariable = 10; 3690 3691 3692 /** 3693 * @deprecated replaced by {@link EDbObjectType#method}. 3694 * column method like SetXY below, column method in {@link #methodToken}, and colomn name in {@link #partToken}. 3695 *<p> UPDATE Cities 3696 *<p> SET Location.SetXY(23.5, 23.5) 3697 * 3698 * 3699 */ 3700 public final static int ttobjColumnMethod = 11; 3701 3702 /** 3703 * Named argument parameter name in function calls. 3704 * <p>Example: In Snowflake FLATTEN(INPUT => parse_json(col), outer => TRUE), 3705 * "INPUT" and "outer" are named argument parameter names, NOT column references. 3706 * <p>These should be skipped during column resolution and data lineage analysis. 3707 */ 3708 public final static int ttobjNamedArgParameter = 12; 3709 3710 /** 3711 * @deprecated replaced by {@link EDbObjectType}. 3712 * function name in {@link #objectToken} 3713 */ 3714 public final static int ttobjFunctionName = 13; 3715 3716 3717 /** 3718 * @deprecated replaced by {@link EDbObjectType#constraint}. 3719 * constraint name in {@link #objectToken} 3720 */ 3721 public final static int ttobjConstraintName = 19; 3722 3723 /** 3724 * @deprecated replaced by {@link EDbObjectType}. 3725 * string constant in {@link #objectToken} 3726 */ 3727 public final static int ttobjStringConstant = 23; 3728 3729 3730 /** 3731 * @deprecated replaced by {@link EDbObjectType}. 3732 * attribute name is in {@link #partToken} 3733 */ 3734 public final static int ttobjAttribute = 26; 3735 3736 3737 /** 3738 * @deprecated replaced by {@link EDbObjectType}. 3739 * datatype was not represented by a TObjectName object, this constant was used in source tokens that consist of TTypeName. 3740 */ 3741 public final static int ttobjDatatype = 30; 3742 3743 /** 3744 * @deprecated replaced by {@link EDbObjectType}. 3745 * schema name in {@link #schemaToken} 3746 */ 3747 public final static int ttobjSchemaName = 31; 3748 3749 3750 /** 3751 * @deprecated replaced by {@link EDbObjectType}. 3752 * postgresql 3753 * Positional Parameters, $1, $1[1], $1[1,10] 3754 * parameter name is in {@link #partToken} of $1, 3755 * and parameter name is in {@link #objectToken} of $1.columnName, 3756 * and column name is in {@link #partToken} 3757 */ 3758 3759 public final static int ttobjPositionalParameters = 61; 3760 3761 3762 3763 private void setColumnTokenOfPositionalParameters(TSourceToken column){ 3764 this.objectToken = this.partToken; 3765 this.partToken = column; 3766 } 3767 3768 private int objectType = ttobjUnknown; 3769 3770 3771 /** 3772 * @deprecated replaced by {@link EDbObjectType}. 3773 * this type is used in TObjectNameList, when objects in TObjectNameList includes more than 3774 * one type, objtype of that TObjectNameList was set to ttobjMixed. 3775 * 3776 * removed since v2.9.2.5 3777 */ 3778 // public final static int ttobjMixed = 100; 3779 3780 /** 3781 * @deprecated replaced by {@link EDbObjectType#library}. 3782 * removed since v2.9.2.5 3783 */ 3784 // public final static int ttObjLibrary = 72; 3785 3786 /** 3787 * @deprecated replaced by {@link EDbObjectType#oracleHint}. 3788 * removed since v2.9.2.5 3789 */ 3790 // public final static int ttObjOracleHint = 70; 3791 3792 /** 3793 * @deprecated replaced by {@link EDbObjectType#fieldName}. 3794 * check {@link gudusoft.gsqlparser.nodes.TExpression#getFieldName()} for more 3795 * removed since v2.9.2.5 3796 */ 3797 // public final static int ttobjFieldName = 51; 3798 3799 /** 3800 * @deprecated replaced by {@link EDbObjectType#miningModel}. 3801 * removed since v2.9.2.5 3802 */ 3803 // public final static int ttobjMiningModel = 46; 3804 3805 /** 3806 * @deprecated replaced by {@link EDbObjectType#materializedView}. 3807 * removed since v2.9.2.5 3808 */ 3809 // public final static int ttobjMaterializedView = 44; 3810 3811 /** 3812 * @deprecated replaced by {@link EDbObjectType#indextype}. 3813 * removed since v2.9.2.5 3814 */ 3815 // public final static int ttobjIndexType = 42; 3816 3817 /** 3818 * @deprecated replaced by {@link EDbObjectType#operator}. 3819 * removed since v2.9.2.5 3820 */ 3821 // public final static int ttobjOperator = 40; 3822 3823 /** 3824 * @deprecated replaced by {@link EDbObjectType#server}. 3825 * server name in {@link #serverToken} 3826 * 3827 * removed since v2.9.2.5 3828 */ 3829 // public final static int ttobjServerName = 32; 3830 3831 /** 3832 * @deprecated replaced by {@link EDbObjectType#sequence}. 3833 * Sequence name in {@link #objectToken} 3834 * 3835 * removed since v2.9.2.5 3836 */ 3837 // public final static int ttobjSequence = 29; 3838 3839 /** 3840 * @deprecated replaced by {@link EDbObjectType#plsql_package}. 3841 * package name in {@link #objectToken} 3842 * 3843 * removed since v2.9.2.5 3844 */ 3845 // public final static int ttobjPackage = 28; 3846 3847 /** 3848 * @deprecated replaced by {@link EDbObjectType#alias}. 3849 * alias name in {@link #objectToken} 3850 * 3851 * removed since v2.9.2.5 3852 */ 3853 // public final static int ttobjAliasName = 25; 3854 3855 3856 /** 3857 * @deprecated replaced by {@link EDbObjectType#trigger}. 3858 * Trigger name in {@link #objectToken} 3859 * 3860 * removed since v2.9.2.5 3861 */ 3862 // public final static int ttobjTrigger = 24; 3863 3864 /** 3865 * @deprecated replaced by {@link EDbObjectType#database}. 3866 * Database name in {@link #objectToken} 3867 * 3868 * removed since v2.9.2.5 3869 */ 3870 // public final static int ttobjDatabaseName = 22; 3871 3872 /** 3873 * @deprecated replaced by {@link EDbObjectType#transaction}. 3874 * Transaction name in {@link #objectToken} 3875 * 3876 * removed since v2.9.2.5 3877 */ 3878 // public final static int ttobjTransactionName = 21; 3879 3880 3881 /** 3882 * @deprecated replaced by {@link EDbObjectType#user_defined_type}. 3883 * type name in {@link #objectToken} 3884 * 3885 * removed since v2.9.2.5 3886 */ 3887 // public final static int ttobjTypeName = 27; 3888 3889 /** 3890 * @deprecated replaced by {@link EDbObjectType#property}. 3891 * property name in {@link #propertyToken} 3892 * 3893 * removed since v2.9.2.5 3894 */ 3895 // public final static int ttobjPropertyName = 20; 3896 3897 /** 3898 * @deprecated replaced by {@link EDbObjectType#view}. 3899 * view name in {@link #objectToken} 3900 * 3901 * removed since v2.9.2.5 3902 */ 3903 // public final static int ttobjViewName = 18; 3904 3905 /** 3906 * @deprecated replaced by {@link EDbObjectType#cursor}. 3907 * cursor name in {@link #objectToken} 3908 * 3909 * removed since v2.9.2.5 3910 */ 3911 // public final static int ttobjCursorName = 17; 3912 3913 /** 3914 * @deprecated replaced by {@link EDbObjectType#materializedView}. 3915 * materialized view name in {@link #objectToken} 3916 * 3917 * removed since v2.9.2.5 3918 */ 3919 // public final static int ttobjMaterializedViewName = 16; 3920 3921 /** 3922 * @deprecated replaced by {@link EDbObjectType#index}. 3923 * index name in {@link #objectToken} 3924 * 3925 * removed since v2.9.2.5 3926 */ 3927 // public final static int ttobjIndexName = 15; 3928 3929 /** 3930 * @deprecated replaced by {@link EDbObjectType#label}. 3931 * label name in {@link #objectToken} 3932 * 3933 * removed since v2.9.2.5 3934 */ 3935 // public final static int ttobjLabelName = 14; 3936 3937 /** 3938 * @deprecated replaced by {@link EDbObjectType#procedure}. 3939 * procedure name in {@link #objectToken} 3940 * 3941 * removed since v2.9.2.5 3942 */ 3943 // public final static int ttobjProcedureName = 12; 3944 3945 /** 3946 * @deprecated replaced by {@link EDbObjectType#variable}. 3947 * table variable in objectToken. 3948 * 3949 * removed since v2.9.2.5 3950 */ 3951 // public final static int ttobjTableVar = 8; 3952 3953 /** 3954 * @deprecated replaced by {@link EDbObjectType#cte}. 3955 * table name in objectToken. 3956 * 3957 * removed since v2.9.2.5 3958 */ 3959 // public final static int ttobjTableCTE = 5; 3960 3961 /** 3962 * @deprecated replaced by {@link EDbObjectType}. 3963 * table name in objectToken. 3964 */ 3965 // public final static int ttobjTableTemp = 6; 3966 3967 /** 3968 * @deprecated replaced by {@link EDbObjectType}. 3969 */ 3970 // public final static int ttobjTablePivot = 7; 3971 3972 /** 3973 * @deprecated replaced by {@link EDbObjectType#table_alias}. 3974 * table alias in objectToken 3975 * 3976 * removed since v2.9.2.5 3977 */ 3978 // public final static int ttObjTableAlias = 4; 3979}