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 dbvdameng: 1037 if ( (getColumnNameOnly().compareToIgnoreCase ("sysdate") == 0) 1038 || (getColumnNameOnly().compareToIgnoreCase ("nextval") == 0) 1039 || (getColumnNameOnly().compareToIgnoreCase ("rownum") == 0) 1040 || (getColumnNameOnly().compareToIgnoreCase ("level") == 0) 1041 || (getColumnNameOnly().compareToIgnoreCase ("user") == 0) 1042 ){ 1043 setObjectType(TObjectName.ttobjNotAObject); 1044 lcResult = false; 1045 } 1046 if ((toString().startsWith(":"))&&(toString().indexOf(".") == -1)) 1047 { 1048 setObjectType(TObjectName.ttobjNotAObject); 1049 lcResult = false; 1050 } 1051 break; 1052 case dbvmssql: 1053 if ((getColumnNameOnly().compareToIgnoreCase ("system_user") == 0) 1054 ){ 1055 //setObjectType(TObjectName.ttobjNotAObject); 1056 lcResult = false; 1057 } 1058 break; 1059 case dbvmysql: 1060 if (toString().startsWith("\"")){ 1061 // "X" is a string literal 1062 lcResult = false; 1063 } 1064 // Skip reserved keyword check if the token was converted to IDENT (264) 1065 // by the yyparse() keyword-as-column-name lookahead 1066 if (getPartToken() != null && getPartToken().tokencode == 264) { 1067 break; 1068 } 1069 if (keywordChecker.isKeyword(toString(),EDbVendor.dbvmysql,"6.0",true)){ 1070 isReservedKeyword = true; 1071 lcResult = false; 1072 } 1073 break; 1074 case dbvteradata: 1075 if ((getObjectString().length() == 0)&&((getColumnNameOnly().compareToIgnoreCase ("account") == 0) 1076 ||(getColumnNameOnly().compareToIgnoreCase ("current_date") == 0) 1077 ||(getColumnNameOnly().compareToIgnoreCase ("current_role") == 0) 1078 ||(getColumnNameOnly().compareToIgnoreCase ("current_time") == 0) 1079 ||(getColumnNameOnly().compareToIgnoreCase ("current_timestamp") == 0) 1080 ||(getColumnNameOnly().compareToIgnoreCase ("current_user") == 0) 1081 ||(getColumnNameOnly().compareToIgnoreCase ("database") == 0) 1082 ||((getColumnNameOnly().compareToIgnoreCase ("date") == 0)&&( this.getDbObjectType() != EDbObjectType.column )) 1083 ||(getColumnNameOnly().compareToIgnoreCase ("profile") == 0) 1084 ||(getColumnNameOnly().compareToIgnoreCase ("role") == 0) 1085 ||(getColumnNameOnly().compareToIgnoreCase ("session") == 0) 1086 ||(getColumnNameOnly().compareToIgnoreCase ("time") == 0) 1087 ||(getColumnNameOnly().compareToIgnoreCase ("user") == 0) 1088 ||(getColumnNameOnly().compareToIgnoreCase ("sysdate") == 0) 1089 )){ 1090 lcResult = false; 1091 } 1092 break; 1093 case dbvpostgresql: 1094 if (toString().startsWith("$")){ 1095 if ((toString().charAt(1) >= '0') 1096 &&(toString().charAt(1) <= '9')){ 1097 this.setDbObjectType(EDbObjectType.variable); 1098 lcResult = false; 1099 } 1100 } 1101 break; 1102 case dbvbigquery: 1103 if ((getColumnNameOnly().compareToIgnoreCase ("CURRENT_DATE") == 0) 1104 ||(getColumnNameOnly().compareToIgnoreCase ("CURRENT_TIME") == 0) 1105 ||(getColumnNameOnly().compareToIgnoreCase ("CURRENT_TIMESTAMP") == 0) 1106 ){ 1107 //setObjectType(TObjectName.ttobjNotAObject); 1108 lcResult = false; 1109 } 1110 break; 1111 } 1112 1113 if(lcResult){ 1114 validate_column_status = TBaseType.VALIDATED_CAN_BE_A_COLUMN_NAME; 1115 }else{ 1116 validate_column_status = TBaseType.VALIDATED_CAN_NOT_BE_A_COLUMN_NAME; 1117 } 1118 1119 return lcResult; 1120 1121 } 1122 1123 /** 1124 * Check if a string represents a numeric literal (integer or decimal). 1125 * Examples: "5", "10", "3.14", ".5" 1126 */ 1127 private static boolean isNumericLiteral(String text) { 1128 if (text == null || text.isEmpty()) return false; 1129 boolean hasDigit = false; 1130 boolean hasDot = false; 1131 for (int i = 0; i < text.length(); i++) { 1132 char c = text.charAt(i); 1133 if (c >= '0' && c <= '9') { 1134 hasDigit = true; 1135 } else if (c == '.' && !hasDot) { 1136 hasDot = true; 1137 } else { 1138 return false; 1139 } 1140 } 1141 return hasDigit; 1142 } 1143 1144 private boolean expandStarColumns = false; 1145 ArrayList<String> expandedStarColumns = new ArrayList<>(); 1146 1147 public ArrayList<String> getColumnsLinkedToStarColumn() { 1148 if (expandStarColumns) return expandedStarColumns; 1149 1150 //TTable sourceTable = this.getSourceTable(); 1151 if (getSourceTableList().size() > 0){ 1152 for( int i = 0;i < getSourceTableList().size();i++){ 1153 for(String c:getSourceTableList().get(i).getExpandedStarColumns()){ 1154 expandedStarColumns.add(c); 1155 } 1156 } 1157 } 1158 1159 expandStarColumns = true; 1160 return expandedStarColumns; 1161 1162 } 1163 1164 private ArrayList<String> columnsLinkedToStarColumn = new ArrayList<String>(); 1165 1166 private TResultColumnList columnsLinkedToStar; 1167 1168 public void setColumnsLinkedToStar(TResultColumnList columnsLinkedToStar) { 1169 this.columnsLinkedToStar = columnsLinkedToStar; 1170 for(TResultColumn rc:columnsLinkedToStar){ 1171 if (rc.getColumnAlias()!= ""){ 1172 columnsLinkedToStarColumn.add(rc.getColumnAlias()); 1173 }else{ 1174 columnsLinkedToStarColumn.add(rc.getColumnNameOnly()); 1175 } 1176 } 1177 1178 this.setDbObjectTypeDirectly(EDbObjectType.column); 1179 } 1180 1181 /** 1182 * if this is a star column(column name is *), and the value of this star column 1183 * is derived from a subquery, then, this field points to the select list in the subquery 1184 * 1185 * @return the select list in the subquery 1186 */ 1187 public TResultColumnList getColumnsLinkedToStar() { 1188 return columnsLinkedToStar; 1189 } 1190 1191 /** 1192 * Set the table this column belongs to. Used by parser internally. 1193 * 1194 * @param sourceTable table contains this column 1195 */ 1196 public void setSourceTable(TTable sourceTable) { 1197 1198 // INSERT INTO "omni"."omni_upload_t1a8067802b804755b1d29ee935b3b0bc" VALUES ($1, $2, $3, $4, $5) 1199 // if this objectname is $1, then just return 1200 if (this.getDbObjectType() == EDbObjectType.parameter) { 1201 // to avoid this parameter been checked in TAttributeResolver preVisit(TObjectName attribute) 1202 this.setValidate_column_status(TBaseType.COLUMN_LINKED_TO_TABLE_IN_OLD_ALGORITHM); 1203 return; 1204 } 1205 1206 this.sourceTable = sourceTable; 1207 // 如果 column token 的 tokentype 为 ETokenType.ttkeyword, 那么调整为 ETokenType.ttidentifier 1208 if ((this.getPartToken() != null)&&(this.getPartToken().tokentype == ETokenType.ttkeyword)){ 1209 if ((this.getPartToken().getDbObjectType() == EDbObjectType.column)||(this.getPartToken().getDbObjectType() == EDbObjectType.unknown)){ 1210 this.getPartToken().tokentype = ETokenType.ttidentifier; 1211 } 1212 } 1213 this.setDbObjectTypeDirectly(EDbObjectType.column); 1214 1215 // If this column was previously an orphan and is now linked to a table, 1216 // remove it from the orphanColumns list of its owning statement 1217 if (sourceTable != null && this.isOrphanColumn() && this.getOwnStmt() != null) { 1218 TObjectNameList orphanColumns = this.getOwnStmt().getOrphanColumns(); 1219 if (orphanColumns != null) { 1220 orphanColumns.removeElement(this); 1221 } 1222 this.setOrphanColumn(false); 1223 } 1224 } 1225 1226 public void setSourceTableBySQLResolver(TCustomSqlStatement sqlStatement, TAttributeNode attributeNode, TTable newSourceTable) { 1227 // 如果 column token 的 tokentype 为 ETokenType.ttkeyword, 那么调整为 ETokenType.ttidentifier 1228 if ((this.getPartToken() != null)&&(this.getPartToken().tokentype == ETokenType.ttkeyword)){ 1229 if ((this.getPartToken().getDbObjectType() == EDbObjectType.column)||(this.getPartToken().getDbObjectType() == EDbObjectType.unknown)){ 1230 this.getPartToken().tokentype = ETokenType.ttidentifier; 1231 } 1232 } 1233 1234 if ((this.sourceTable != null) && (this.sourceTable.equals(newSourceTable))) return; 1235 1236 if ((this.getResolveStatus() == TBaseType.RESOLVED_AND_FOUND ) 1237 || (newSourceTable.getTableType() != ETableSource.subquery)){// 关联到 subquery 的 column 本能算真正找到 table,因此还不能去掉 orphan column 1238 if (sqlStatement.getOrphanColumns().removeElement(this)){ 1239 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){ 1240 TBaseType.log(String.format("Remove orphan column <%s> find in old algorithm",this.toString()),TLog.WARNING,this); 1241 } 1242 // remove the waring in sql statement's error syntax list 1243 TCustomSqlStatement currentStatement = sqlStatement; 1244 while (currentStatement != null) { 1245 if (currentStatement.getSyntaxHints() != null) { 1246 for(int i=0; i<currentStatement.getSyntaxHints().size(); i++) { 1247 TSyntaxError syntaxError = currentStatement.getSyntaxHints().get(i); 1248 if (syntaxError.errortype == EErrorType.sphint) { 1249 if ((syntaxError.lineNo == this.getStartToken().lineNo)||(syntaxError.columnNo == this.getStartToken().columnNo)) { 1250 currentStatement.getSyntaxHints().remove(i); 1251 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){ 1252 TBaseType.log(String.format("Remove orphan column <%s> warning message in old algorithm", this.toString()), TLog.WARNING, this); 1253 } 1254 break; 1255 } 1256 } 1257 } 1258 } 1259 currentStatement = currentStatement.getParentStmt(); 1260 } 1261 } 1262 }else{ 1263 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); 1264 } 1265 1266 if (this.sourceTable != null){ 1267 if (this.sourceTable.getLinkedColumns().removeElement(this)){ 1268 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){ 1269 TBaseType.log(String.format("Remove <%s> at addr: %s from table <%s> that found in old algorithm, new linked table is: %s" 1270 ,this.toString(),Integer.toHexString(this.hashCode()),this.sourceTable.toString(),newSourceTable.toString()) 1271 ,TLog.WARNING,this); 1272 } 1273 } 1274 } 1275 this.sourceTable = newSourceTable; 1276 this.sourceTable.getLinkedColumns().addObjectName(this); 1277 1278 if (this.getSourceColumn() == null){ 1279 // if (attributeNode.isAttributeCreatedFromAliasColumn()){ 1280 this.setSourceColumn(attributeNode.getSubLevelResultColumn()); 1281 // } 1282 } 1283 1284 this.setDbObjectTypeDirectly(EDbObjectType.column); 1285 } 1286 1287 /** 1288 * Get the <b>immediate</b> source table where this column is visible in the current scope. 1289 * 1290 * <p>This returns the table/subquery/CTE that directly exposes this column in the FROM clause, 1291 * NOT the final physical table after tracing through subqueries or CTEs.</p> 1292 * 1293 * <p>To get the final physical table (after tracing through all layers), use: 1294 * {@code getResolution().getColumnSource().getFinalTable()}</p> 1295 * 1296 * <h3>Example</h3> 1297 * <pre>{@code 1298 * SELECT title FROM (SELECT * FROM books) sub 1299 * 1300 * For the 'title' column in outer SELECT: 1301 * - getSourceTable() → TTable for subquery 'sub' (tableType=subquery) 1302 * - resolution.getFinalTable() → TTable for 'books' (the physical table) 1303 * }</pre> 1304 * 1305 * @return The immediate source table, or null if not resolved 1306 * @see #sourceTable 1307 * @see gudusoft.gsqlparser.resolver2.model.ColumnSource#getFinalTable() 1308 */ 1309 public TTable getSourceTable() { 1310 // If new resolver determined this column is ambiguous, don't return old Phase 1 value 1311 // This ensures ambiguous columns are treated as orphan by the formatter 1312 // EXCEPTION: Star columns (*) should preserve their sourceTable for proper output 1313 if (resolution != null && resolution.isAmbiguous()) { 1314 String colName = getColumnNameOnly(); 1315 if (colName != null && colName.equals("*")) { 1316 // Star columns keep their Phase 1 sourceTable 1317 return sourceTable; 1318 } 1319 if (gudusoft.gsqlparser.TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE) { 1320 System.out.println("[TObjectName.getSourceTable] Column '" + colName + 1321 "' is AMBIGUOUS - returning null instead of " + 1322 (sourceTable != null ? sourceTable.getName() : "null")); 1323 } 1324 return null; 1325 } 1326 if (resolution == null && sourceTable != null) { 1327 if (gudusoft.gsqlparser.TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE) { 1328 System.out.println("[TObjectName.getSourceTable] Column '" + getColumnNameOnly() + 1329 "' has NO resolution - using Phase 1 sourceTable: " + sourceTable.getName()); 1330 } 1331 } 1332 return sourceTable; 1333 } 1334 1335 public void setResolveStatus(int resolveStatus) { 1336 this.resolveStatus = resolveStatus; 1337 1338 if (this.resolveStatus == TBaseType.RESOLVED_AND_FOUND){ 1339 this.setDbObjectTypeDirectly(EDbObjectType.column); 1340 } 1341 1342 } 1343 1344 private int resolveStatus = TBaseType.NOT_RESOLVED_YET; 1345 1346 public int getResolveStatus() { 1347 return resolveStatus; 1348 } 1349 1350 public void TObjectName(){ 1351 1352 } 1353 1354 private TObjectNameList columnAttributes = null; 1355 1356 private boolean subscripts; 1357 private TIndirection indirection; 1358 1359 /** 1360 * PostgreSQL column with array types 1361 * <pre> 1362 * CREATE TABLE sal_emp ( 1363 * name text, 1364 * pay_by_quarter integer[], 1365 * schedule text[][] 1366 * ); 1367 * </pre> 1368 * In the above SQL, this method returns true for <code>pay_by_quarter</code> column. 1369 * 1370 * @return true if this objectName is array type 1371 */ 1372 public boolean isSubscripts() { 1373 return subscripts; 1374 } 1375 1376 public void setIndirection(TIndirection indirection) { 1377 if(indirection == null) return; 1378 1379 this.indirection = indirection; 1380 // setup the exceptReplaceClause of the last indirection to the parent object which is set in the .y bnf file 1381 if (indirection.getIndices() != null){ 1382 if (indirection.getIndices().getElement(indirection.getIndices().size()-1).getAttributeName() != null){ 1383 setExceptReplaceClause(indirection.getIndices().getElement(indirection.getIndices().size()-1).getAttributeName().getExceptReplaceClause()); 1384 1385 } 1386 } 1387 1388 // possible syntax, support in postgresql only in current version: 1389 // [ indirection ], list in [] was indirection 1390 // 1391 // tablename[.column] 1392 // tablename[.*] 1393 // $1[.somecolumn] 1394 // 1395 // mytable[.arraycolumn[4]] 1396 // mytable[.two_d_column[17][34]] 1397 // $1[[10:42]] 1398 // 1399 1400 if (this.getObjectType() == TObjectName.ttobjPositionalParameters){ 1401 if(indirection.isRealIndices()){ 1402 //$1[10:42] 1403 this.subscripts = true; 1404 }else{ 1405 //$1.somecolumn 1406 this.setColumnTokenOfPositionalParameters(indirection.getIndices().getElement(0).getAttributeName().getPartToken()); 1407 } 1408 }else{ 1409 if(indirection.isRealIndices()){ 1410 if (indirection.getIndices().size() == 1){ 1411 // arraycolumn[4] 1412 this.subscripts = true; 1413 }else if (indirection.getIndices().size() >= 2){ 1414 if (!indirection.getIndices().getElement(0).isRealIndices()){ 1415 // mytable[.arraycolumn[4]] 1416 // mytable[.two_d_column[17][34]] 1417 // this.setPartTokenOfIndirection(indirection.getIndices().getElement(0).getAttributeName().getPartToken()); 1418 this.subscripts = true; 1419 // this.indirection.getIndices().remove(0); 1420 } 1421 } 1422 } 1423 1424 //else{ 1425 // 首先查找 : 和 [ 分隔符,如果找到,在该分隔符前的是 column,如果没有找到按照一般 qualified name 规则处理 1426 // https://docs.snowflake.com/en/user-guide/querying-semistructured.html 1427 int elementIndex = -1; 1428 for(int i=0;i<indirection.getIndices().size();i++){ 1429 TIndices tmp = indirection.getIndices().getElement(i); 1430 if ((tmp.getStartToken().tokencode == ':')||(tmp.getStartToken().tokencode == '[')||(tmp.getStartToken().tokencode == TBaseType.bind_v)){ 1431 elementIndex = i; 1432 break; 1433 } 1434 } 1435 if (elementIndex >= 0){ 1436 // 找到了 : 和 [ 分隔符 1437 if (elementIndex == 0){ 1438 // snowflake, <column>:<level1_element> 1439 // everything already perfect, nothing need to be changed 1440 partToken.setDbObjectType(EDbObjectType.column); 1441 }else if (elementIndex == 1){ 1442 // snowflake, table.column:<level1_element> 1443 objectToken = partToken; 1444 objectToken.setDbObjectType(EDbObjectType.table); 1445 partToken = indirection.getIndices().getElement(elementIndex-1).getAttributeName().getPartToken(); 1446 partToken.setDbObjectType(EDbObjectType.column); 1447 }else if (elementIndex == 2){ 1448 // snowflake, schema.table.column:<level1_element> 1449 schemaToken = partToken; 1450 schemaToken.setDbObjectType(EDbObjectType.schema); 1451 objectToken = indirection.getIndices().getElement(elementIndex-2).getAttributeName().getPartToken(); 1452 objectToken.setDbObjectType(EDbObjectType.table); 1453 partToken = indirection.getIndices().getElement(elementIndex-1).getAttributeName().getPartToken(); 1454 partToken.setDbObjectType(EDbObjectType.column); 1455 } 1456 }else{ 1457 // 一般 qualified name 规则处理 1458 if (indirection.getIndices().size() == 1){ 1459 this.setPartTokenOfIndirection(indirection.getIndices().getElement(0).getAttributeName().getPartToken()); 1460 }else if (indirection.getIndices().size() == 2){ 1461 schemaToken = partToken; 1462 schemaToken.setDbObjType(TObjectName.ttobjSchemaName); 1463 objectToken = indirection.getIndices().getElement(0).getAttributeName().getPartToken(); 1464 objectToken.setDbObjType(ttobjTable); 1465 partToken = indirection.getIndices().getElement(1).getAttributeName().getPartToken(); 1466 partToken.setDbObjType(ttobjColumn); 1467 }else if (indirection.getIndices().size() == 3){ 1468 // db.schema.tablename.column 1469 databaseToken = partToken; 1470 databaseToken.setDbObjectType(EDbObjectType.database); 1471 partToken = indirection.getIndices().getElement(2).getAttributeName().getPartToken(); 1472 partToken.setDbObjectType(EDbObjectType.column); 1473 objectToken = indirection.getIndices().getElement(1).getAttributeName().getPartToken(); 1474 objectToken.setDbObjectType(EDbObjectType.table); 1475 schemaToken = indirection.getIndices().getElement(0).getAttributeName().getPartToken(); 1476 schemaToken.setDbObjectType(EDbObjectType.schema); 1477 } 1478 } 1479 1480// if (indirection.getIndices().size() == 1){ 1481// if ((indirection.getIndices().getElement(0).getStartToken().tokencode == ':') 1482// ||(indirection.getIndices().getElement(0).getStartToken().tokencode == TBaseType.bind_v)) 1483// { 1484// // snowflake, <column>:<level1_element> 1485// 1486// }else{ 1487// // tablename[.column] 1488// // tablename[.*] 1489// this.setPartTokenOfIndirection(indirection.getIndices().getElement(0).getAttributeName().getPartToken()); 1490// } 1491// }else if (indirection.getIndices().size() == 2){ 1492// if ((indirection.getIndices().getElement(0).getStartToken().tokencode == ':') 1493// ||(indirection.getIndices().getElement(0).getStartToken().tokencode == TBaseType.bind_v)) 1494// { 1495// // snowflake, <column>:<level1_element> 1496// 1497// }else { 1498// // schema.tablename.column 1499// schemaToken = partToken; 1500// schemaToken.setDbObjType(TObjectName.ttobjSchemaName); 1501// objectToken = indirection.getIndices().getElement(0).getAttributeName().getPartToken(); 1502// objectToken.setDbObjType(ttobjTable); 1503// partToken = indirection.getIndices().getElement(1).getAttributeName().getPartToken(); 1504// partToken.setDbObjType(ttobjColumn); 1505// } 1506// }else if (indirection.getIndices().size() == 3){ 1507// // db.schema.tablename.column 1508// databaseToken = partToken; 1509// databaseToken.setDbObjectType(EDbObjectType.database); 1510// partToken = indirection.getIndices().getElement(2).getAttributeName().getPartToken(); 1511// partToken.setDbObjectType(EDbObjectType.column); 1512// objectToken = indirection.getIndices().getElement(1).getAttributeName().getPartToken(); 1513// objectToken.setDbObjectType(EDbObjectType.table); 1514// schemaToken = indirection.getIndices().getElement(0).getAttributeName().getPartToken(); 1515// schemaToken.setDbObjectType(EDbObjectType.schema); 1516// } 1517 1518 // } 1519 1520 } 1521 1522 } 1523 1524 /** 1525 * Array element of this objectName 1526 * <pre> 1527 * select arraycolumn[4] from t; 1528 * </pre> 1529 * In the above SQL, this method returns <code>[4]</code> of this objectName. 1530 * 1531 * @return array element of this objectName 1532 * @see gudusoft.gsqlparser.nodes.TIndirection 1533 */ 1534 public TIndirection getIndirection() { 1535 return indirection; 1536 } 1537 1538 private void setPartTokenOfIndirection(TSourceToken column){ 1539 parseTablename(); 1540 this.partToken = column; 1541 this.partToken.setDbObjType(ttobjColumn); 1542 } 1543 1544 public void setPropertyToken(TSourceToken propertyToken) { 1545 this.propertyToken = propertyToken; 1546 } 1547 1548 public TSourceToken getAtsign() { 1549 return atsign; 1550 } 1551 1552 public TSourceToken getMethodToken() { 1553 1554 return methodToken; 1555 } 1556 1557 public TSourceToken getPropertyToken() { 1558 return propertyToken; 1559 } 1560 1561 /** 1562 * The server part of this objectName: [server.][database.][schema.]object 1563 * 1564 * @return server part of the objectName 1565 */ 1566 public TSourceToken getServerToken() { 1567 return serverToken; 1568 } 1569 1570 public TSourceToken getExclamationmark() { 1571 1572 return exclamationmark; 1573 } 1574 1575 /** 1576 * 1577 * The database link part <code>remoreserver</code> in this objectName: scott.emp@remoreserver 1578 * 1579 * @return database link 1580 */ 1581 public TObjectName getDblink() { 1582 1583 return dblink; 1584 } 1585 1586 /** 1587 * The database part of this objectName: [server.][database.][schema.]object 1588 * 1589 * @return database part of the objectName 1590 */ 1591 public TSourceToken getDatabaseToken() { 1592 return databaseToken; 1593 } 1594 1595 1596 private boolean tableDetermined = true; 1597 1598 public void setTableDetermined(boolean tableDetermined) { 1599 this.tableDetermined = tableDetermined; 1600 } 1601 1602 1603 /** 1604 * Sometime, a non-qualified column can't be linked to a table without additional metadata from database. 1605 * <pre> 1606 * select name from emp, dept 1607 * </pre> 1608 * In the above SQL, the <code>name</code> column can't be determined which table it belongs to. 1609 * 1610 * Below is a more complicated SQL that shows the relationship between column and table. 1611 * <pre> 1612 * select 1613 * s2.s2t1a1, 1614 * s3.s3t1a1 1615 * from 1616 * ( 1617 * select * 1618 * from subselect2table1 s2t1 1619 * ) s2, 1620 * ( 1621 * select * 1622 * from subselect3table1, subselect3table2 1623 * ) s3 1624 * </pre> 1625 * 1626 * column s2t1a1 was linked to subselect2table1, {@link #isTableDetermined()} returns true for this column. 1627 * <br> column s3t1a1 was linked to both subselect3table1 and subselect3table2 1628 * due to lack of meta information from database, {@link #isTableDetermined()} returns false for this column. 1629 * <p> 1630 * Provide database metadata will help GSP links the column to the table correctly. 1631 * 1632 * @return true if this column can be linked to a table without doubt. 1633 * @see gudusoft.gsqlparser.TGSqlParser#setMetaDatabase 1634 */ 1635 public boolean isTableDetermined() { 1636 return tableDetermined; 1637 } 1638 1639 /** 1640 * used in Oracle and teradata SQL syntax 1641 * <p>teradata: 1642 * <p>column.attribute() 1643 * <p>column.attribute().attribute() 1644 * @param attributes 1645 */ 1646 public void attributesToPropertyToken(TObjectNameList attributes){ 1647 if (attributes.size() == 1){ 1648 this.propertyToken = attributes.getObjectName(0).getPartToken(); 1649 } 1650 } 1651 1652 public void setColumnAttributes(TObjectNameList columnAttributes) { 1653 this.columnAttributes = columnAttributes; 1654 } 1655 1656 /** 1657 * The data type of this column is structured UDT, this method returns the column's attributes. 1658 * 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>. 1659 * <pre> 1660 * 1661 * CREATE TYPE school_record AS ( 1662 * school_name VARCHAR(20), 1663 * GPA FLOAT); 1664 * 1665 * CREATE TYPE college_record AS ( 1666 * school school_record, 1667 * major VARCHAR(20), 1668 * minor VARCHAR(20)); 1669 * 1670 * CREATE TABLE student_record ( 1671 * student_id INTEGER, 1672 * Last_name VARCHAR(20), 1673 * First_name VARCHAR(20), 1674 * high_school school_record, 1675 * college college_record); 1676 * 1677 * SELECT student_id, last_name, first_name, 1678 * high_school.school_name(), high_school.GPA(), 1679 * college.school().school_name(), college.school().GPA(), 1680 * college.major(), college.minor() 1681 * FROM student_record; 1682 * 1683 * SELECT *.ALL FROM student_record; 1684 * SELECT student_record.*.ALL; 1685 * </pre> 1686 * Take this column <code>college.school().school_name()</code> for example, the partToken of this objectName 1687 * should be <code>college</code>, and the value returned by this method should be 1688 * <code>school().school_name()</code> 1689 * <p> 1690 * PLEASE NOTE THAT CURRENT VERSION CAN'T HANDLE THE ABOVE SQL CORRECTLY. 1691 * 1692 * @return attributes of this structured UDT column 1693 */ 1694 public TObjectNameList getColumnAttributes() { 1695 return columnAttributes; 1696 } 1697 1698 /** 1699 * Used internally. 1700 * @deprecated use {@link #setDbObjectType} instead 1701 * 1702 * @param objectType object type of this objectName 1703 */ 1704 public void setObjectType(int objectType) { 1705 if (this.objectType == objectType) return; 1706 this.objectType = objectType; 1707 // set this object type to source token 1708 switch(this.getObjectType()){ 1709 case TObjectName.ttobjTable: 1710 // case TObjectName.ttobjTableTemp: 1711 // case TObjectName.ttobjTableVar: 1712 this.parseTablename(); 1713 this.objectToken.setDbObjType(this.objectType); 1714 if (dbObjectType != EDbObjectType.stage){ // not already set to stage 1715 dbObjectType = EDbObjectType.table; 1716 } 1717 1718 if ((!TSQLEnv.supportSchema(this.dbvendor))&&(this.schemaToken != null)){ 1719 this.databaseToken = this.schemaToken; 1720 this.schemaToken = null; 1721 } 1722 break; 1723// case TObjectName.ttobjTableCTE: 1724// this.parseTablename(); 1725// this.objectToken.setDbObjType(this.objectType); 1726// dbObjectType = EDbObjectType.cte; 1727// break; 1728// case ttObjLibrary: 1729// this.parseTablename(); 1730// this.objectToken.setDbObjType(this.objectType); 1731// dbObjectType = EDbObjectType.library; 1732// break; 1733 case TObjectName.ttobjColumn: 1734 this.partToken.setDbObjType(this.objectType); 1735 dbObjectType = EDbObjectType.column; 1736 break; 1737 case TObjectName.ttobjColumnAlias: 1738 if ((this.objectToken == null) && (this.partToken != null)){ 1739 this.parseObjectName(); 1740 } 1741 this.objectToken.setDbObjType(this.objectType); 1742 dbObjectType = EDbObjectType.column_alias; 1743 break; 1744// case TObjectName.ttObjTableAlias: 1745// this.parseObjectName(); 1746// this.objectToken.setDbObjType(this.objectType); 1747// dbObjectType = EDbObjectType.table_alias; 1748// break; 1749 case TObjectName.ttobjParameter: 1750 this.parseObjectName(); 1751 this.objectToken.setDbObjType(this.objectType); 1752 dbObjectType = EDbObjectType.parameter; 1753 break; 1754 case TObjectName.ttobjVariable: 1755 this.parseVariableName(); 1756 this.objectToken.setDbObjType(this.objectType); 1757 dbObjectType = EDbObjectType.variable; 1758 break; 1759 case TObjectName.ttobjColumnMethod: 1760 if (dbObjectType != EDbObjectType.method){ 1761 this.parseColumnMethodName(); 1762 this.partToken.setDbObjType(this.ttobjColumn); 1763 this.methodToken.setDbObjType(this.ttobjColumnMethod); 1764 dbObjectType = EDbObjectType.method; 1765 } 1766 break; 1767// case TObjectName.ttobjProcedureName: 1768// this.parseFunctionName(); 1769// this.objectToken.setDbObjType(this.objectType); 1770// dbObjectType = EDbObjectType.procedure; 1771// break; 1772 case TObjectName.ttobjFunctionName: 1773 this.parseFunctionName(); 1774 this.objectToken.setDbObjType(this.objectType); 1775 dbObjectType = EDbObjectType.function; 1776 break; 1777// case TObjectName.ttobjLabelName: 1778// this.parseObjectName(); 1779// this.objectToken.setDbObjType(this.objectType); 1780// dbObjectType = EDbObjectType.label; 1781// break; 1782// case TObjectName.ttobjIndexName: 1783// this.parseObjectName(); 1784// this.objectToken.setDbObjType(this.objectType); 1785// dbObjectType = EDbObjectType.index; 1786// break; 1787// case TObjectName.ttobjMaterializedViewName: 1788// this.parseObjectName(); 1789// this.objectToken.setDbObjType(this.objectType); 1790// dbObjectType = EDbObjectType.materializedView; 1791// break; 1792// case TObjectName.ttobjViewName: 1793// this.parseObjectName(); 1794// this.objectToken.setDbObjType(this.objectType); 1795// dbObjectType = EDbObjectType.view; 1796// break; 1797// case TObjectName.ttobjCursorName: 1798// this.parseObjectName(); 1799// this.objectToken.setDbObjType(this.objectType); 1800// dbObjectType = EDbObjectType.cursor; 1801// break; 1802 case TObjectName.ttobjConstraintName: 1803 this.parseObjectName(); 1804 this.objectToken.setDbObjType(this.objectType); 1805 dbObjectType = EDbObjectType.constraint; 1806 break; 1807// case TObjectName.ttobjPropertyName: 1808// this.propertyToken.setDbObjType(this.objectType); 1809// dbObjectType = EDbObjectType.property; 1810// break; 1811// case TObjectName.ttobjTransactionName: 1812// this.parseObjectName(); 1813// this.objectToken.setDbObjType(this.objectType); 1814// dbObjectType = EDbObjectType.transaction; 1815// break; 1816// case TObjectName.ttobjDatabaseName: 1817// this.parseObjectName(); 1818// this.objectToken.setDbObjType(this.objectType); 1819// dbObjectType = EDbObjectType.database; 1820// break; 1821 case TObjectName.ttobjStringConstant: 1822 this.parseObjectName(); 1823 this.objectToken.setDbObjType(this.objectType); 1824 break; 1825// case TObjectName.ttobjAliasName: 1826// this.parseObjectName(); 1827// this.objectToken.setDbObjType(this.objectType); 1828// dbObjectType = EDbObjectType.alias; 1829// break; 1830 case TObjectName.ttobjAttribute: 1831 this.partToken.setDbObjType(this.objectType); 1832 dbObjectType = EDbObjectType.attribute; 1833 break; 1834 1835 case TObjectName.ttobjPositionalParameters: 1836 dbObjectType = EDbObjectType.parameter; 1837 break; 1838// case TObjectName.ttobjTypeName: 1839// this.parseObjectName(); 1840// this.objectToken.setDbObjType(this.objectType); 1841// dbObjectType = EDbObjectType.user_defined_type; 1842// break; 1843// case TObjectName.ttobjPackage: 1844// this.parseObjectName(); 1845// this.objectToken.setDbObjType(this.objectType); 1846// dbObjectType = EDbObjectType.plsql_package; 1847// break; 1848// case TObjectName.ttobjSequence: 1849// this.parseObjectName(); 1850// this.objectToken.setDbObjType(this.objectType); 1851// dbObjectType = EDbObjectType.sequence; 1852// break; 1853// case TObjectName.ttobjTrigger: 1854// this.parseObjectName(); 1855// this.objectToken.setDbObjType(this.objectType); 1856// dbObjectType = EDbObjectType.trigger; 1857// break; 1858 default: 1859 break; 1860 } 1861 } 1862 1863 public void setDbObjectType(EDbVendor dbVendor, EDbObjectType dbObjectType) { 1864 this.dbvendor = dbVendor; 1865 this.setDbObjectType(dbObjectType); 1866 } 1867 1868 public void setDbObjectTypeDirectly(EDbObjectType dbObjectType) { 1869 this.dbObjectType = dbObjectType; 1870 } 1871 /** 1872 * Set object type of this objectName 1873 * 1874 * @param dbObjectType database object type 1875 */ 1876 public void setDbObjectType(EDbObjectType dbObjectType) { 1877 if (this.dbObjectType == dbObjectType) return; 1878 if (this.dbObjectType == EDbObjectType.stage) return; 1879 // TODO, 如果已经被设定为某个对象类型,不应该再次设置,但如果下面的语句执行,会导致部分测试用例失败,需要查具体原因 1880 // if (this.dbObjectType != EDbObjectType.unknown) return; 1881 1882 EDbObjectType prev = this.dbObjectType; 1883 this.dbObjectType = dbObjectType; 1884 if (prev == EDbObjectType.unknown){ 1885 switch (dbObjectType){ 1886 case column: 1887 parseColumnName(); 1888 break; 1889 case table: 1890 case index: 1891 case synonym: 1892 case macro: 1893 case view: 1894 case stage: 1895 case task: 1896 case stream: 1897 case TEMP_TABLE: 1898 case pipe: 1899 case security_policy: 1900 1901 case plsql_package: 1902 case trigger: 1903 case transaction: 1904 case user_defined_type: 1905 case property: 1906 case cursor: 1907 case label: 1908 case table_alias: 1909 case partitionScheme: 1910 parseTablename(); 1911 break; 1912 case library: 1913 parseTablename(); 1914 break; 1915 case function: 1916 parseFunctionName(); 1917 break; 1918 case procedure: 1919 case materializedView: 1920 parseFunctionName(); 1921 break; 1922 case alias: 1923 case module: 1924 case sequence: 1925 parseTablename(); 1926 break; 1927 case database: 1928 this.objectToken = this.partToken; 1929 this.databaseToken = null; 1930 this.partToken = null; 1931 break; 1932 case variable: 1933 parseVariableName(); 1934 break; 1935 case schema: 1936 this.databaseToken = this.objectToken; 1937 this.objectToken = this.partToken; 1938 this.schemaToken = this.partToken; 1939 break; 1940 case method: 1941 this.parseColumnMethodName(); 1942 this.partToken.setDbObjType(this.ttobjColumn); 1943 //this.methodToken.setDbObjType(this.ttobjColumnMethod); 1944 this.methodToken.setDbObjectType(EDbObjectType.method); 1945 break; 1946 case cte: 1947 parseObjectName(); 1948 break; 1949 case hint: //sql server hint like nolock 1950 parseObjectName(); 1951 break; 1952 default: 1953 break; 1954 } 1955 } 1956 } 1957 1958 private EDbObjectType dbObjectType = EDbObjectType.unknown; 1959 1960 /** 1961 * The database object type of this objectName such as table, view, column for example. 1962 * If object type is {@link gudusoft.gsqlparser.EDbObjectType#column}, {@link #getPartToken} represents 1963 * the column name, for all other object type, the name of this database object is stored in {@link #getObjectToken} 1964 * 1965 * @return database object type 1966 */ 1967 public EDbObjectType getDbObjectType() { 1968 return dbObjectType; 1969 } 1970 1971 /** 1972 * @deprecated use {@link #getDbObjectType()} instead. 1973 * 1974 * @return the type of database object or variable this objectName represents for. 1975 */ 1976 public int getObjectType() { 1977 1978 return objectType; 1979 } 1980 1981 1982 public void setAtsign(TSourceToken atsign) { 1983 this.atsign = atsign; 1984 } 1985 1986 public void setDblink(TObjectName dblink) { 1987 dblink.setDbObjectType(EDbObjectType.dblink); 1988 this.dblink = dblink; 1989 } 1990 1991 public void setDblink(TObjectName dblink, boolean linkToDB) { 1992 setDblink(dblink); 1993 1994 if (linkToDB){ 1995 if (dblink.numberOfPart == 1){ 1996 this.databaseToken = dblink.getPartToken(); 1997 } 1998 } 1999 } 2000 2001 private TSourceToken serverToken = null; //sql server 2002 private TSourceToken databaseToken = null; //sql server 2003 // schemaToken.objectToken.partToken@dblink, schemaToken, partToken, and dblink is optional 2004 private TSourceToken schemaToken; 2005 private TSourceToken objectToken; 2006 2007 /* 2008 * part is a part of the object. This identifier lets you refer to a part of a schema object, 2009 * such as a column or a partition of a table. Not all types of objects have parts. 2010 */ 2011 private TSourceToken partToken; 2012 private TSourceToken propertyToken = null; 2013 private TSourceToken methodToken = null; 2014 private TSourceToken atsign; //@ 2015 private TObjectName dblink; 2016 2017 // Additional parts for deeply nested struct field access (BigQuery, etc.) 2018 // Stores parts beyond the 6 standard tokens (server, database, schema, object, part, property) 2019 private java.util.List<TSourceToken> additionalParts = null; 2020 2021 // ===== Phase 5: Normalized identifier cache (transient, not serialized) ===== 2022 // These caches reduce repeated normalize() calls for the same TObjectName 2023 private transient String normalizedServer; 2024 private transient String normalizedDatabase; 2025 private transient String normalizedSchema; 2026 private transient String normalizedTable; 2027 private transient String normalizedColumn; 2028 private transient long cacheFingerprint = 0; // Profile fingerprint for cache invalidation 2029 2030 public void setServerToken(TSourceToken serverToken) { 2031 this.serverToken = serverToken; 2032 } 2033 2034 public void setDatabaseToken(TSourceToken databaseToken, boolean implicit) { 2035 this.isImplicitDatabase = implicit; 2036 setDatabaseToken(databaseToken); 2037 } 2038 2039 public void setDatabaseToken(TSourceToken databaseToken) { 2040 this.databaseToken = databaseToken; 2041 } 2042 2043 public void setObjectToken(TSourceToken objectToken) { 2044 this.objectToken = objectToken; 2045 } 2046 2047 public void setPartToken(TSourceToken partToken) { 2048 this.partToken = partToken; 2049 } 2050 2051 public void setMethodToken(TSourceToken methodToken) { 2052 this.methodToken = methodToken; 2053 } 2054 2055 public void setSchemaToken(TSourceToken schemaToken, boolean implicit) { 2056 this.isImplicitSchema = implicit; 2057 setSchemaToken(schemaToken); 2058 } 2059 2060 public void setSchemaToken(TSourceToken schemaToken) { 2061 2062 this.schemaToken = schemaToken; 2063 } 2064 2065 public void setPackageToken(TSourceToken packageToken) { 2066 this.packageToken = packageToken; 2067 } 2068 2069 /** 2070 * Oracle package name 2071 * 2072 * @return the source token of Oracle package name. 2073 */ 2074 public TSourceToken getPackageToken() { 2075 return packageToken; 2076 } 2077 2078 private TSourceToken packageToken = null; 2079 2080 2081 /** 2082 * The object part of this objectName such as table name, view name. 2083 * 2084 * @return object part of this objectName 2085 */ 2086 public TSourceToken getObjectToken() { 2087 return objectToken; 2088 } 2089 2090 /** 2091 * The column name of this objectName if {@link #getDbObjectType} is {@link EDbObjectType#column}. 2092 * {@link #getColumnToken} returns the same value. 2093 * 2094 * @return the column name 2095 */ 2096 public TSourceToken getPartToken() { 2097 return partToken; 2098 } 2099 2100 /** 2101 * The schema name of this objectName. 2102 * 2103 * @return schema name 2104 */ 2105 public TSourceToken getSchemaToken() { 2106 return schemaToken; 2107 } 2108 2109 2110 private String schemaString; 2111 private String objectString; 2112 private String partString; 2113 2114 /** 2115 * String text of the package name. 2116 * 2117 * @return string of the package name,return null if empty. 2118 */ 2119 public String getPackageString(){ 2120 if (getPackageToken() != null) return getPackageToken().toString(); 2121 else return ""; 2122 } 2123 2124 /** 2125 * String text of the server name 2126 * 2127 * @return string of the server name,return null if empty. 2128 */ 2129 public String getServerString(){ 2130 if (getServerToken() != null) return getServerToken().toString(); 2131 else return ""; 2132 } 2133 2134 /** 2135 * String text of the database name 2136 * 2137 * @return string of the database name,return null if empty. 2138 */ 2139 public String getDatabaseString(){ 2140 if (isImplicitDatabase ) return ""; 2141 else if (getDatabaseToken() != null) return getDatabaseToken().toString(); 2142 else if ((this.dbObjectType == EDbObjectType.database) && (getObjectToken() != null)){ 2143 return getObjectToken().toString(); 2144 } 2145 else return ""; 2146 } 2147 2148 /** 2149 * String text of schema name in a qualified name of a schema object. 2150 * 2151 * 2152 * @return string of schema name, return null if empty. 2153 */ 2154 public String getSchemaString() { 2155 if (isImplicitSchema) return ""; 2156 else if (schemaToken != null) 2157 return schemaToken.getAstext(); 2158 else 2159 return "" ; 2160 } 2161 2162 private TSQLEnv sqlEnv = null; 2163 2164 public void setSqlEnv(TSQLEnv sqlEnv) { 2165 this.sqlEnv = sqlEnv; 2166 } 2167 2168 2169 2170 /** 2171 * This is the schema fetched from the SQLEnv. Not the direct qualified schema name of this object 2172 * search this table in the current default database and schema. 2173 * 2174 * If this is a qualified schema object, then return {@link #getSchemaString()} 2175 * 2176 * This method is only valid when the {@link #dbObjectType} is a schema object. 2177 * 2178 * @return schema name fetched from the SQLEnv 2179 */ 2180 public String getImplictSchemaString() { 2181 String implictSchema = null; 2182 // Objects with a db_link refer to remote databases and should not 2183 // inherit the current session's default schema 2184 if (this.dblink != null) return null; 2185 if (this.implictSchemaName != null) return this.implictSchemaName; 2186 2187 if (schemaToken != null) return schemaToken.toString(); 2188 if (getSchemaString().length() > 0) return getSchemaString(); 2189 2190 if (sqlEnv == null) return null; 2191 2192 TSQLSchema s = searchImplicitSchema(); 2193 if (s != null){ 2194 implictSchema = s.getName(); 2195 } 2196 2197 return implictSchema; 2198 } 2199 2200 private String implictDatabaseName; 2201 private String implictSchemaName; 2202 2203 public void setImplictDatabaseName(String implictDatabaseName) { 2204 this.isImplicitDatabase = true; 2205 this.implictDatabaseName = implictDatabaseName; 2206 } 2207 2208 public void setImplictSchemaName(String implictSchemaName) { 2209 this.isImplicitSchema = true; 2210 this.implictSchemaName = implictSchemaName; 2211 } 2212 2213 public String getImplictDatabaseString() { 2214 String implictDatabase = null; 2215 // Objects with a db_link refer to remote databases and should not 2216 // inherit the current session's default catalog 2217 if (this.dblink != null) return null; 2218 if (implictDatabaseName != null) return implictDatabaseName; 2219 2220 if (getDatabaseString().length() > 0) return getDatabaseString(); 2221 2222 if (sqlEnv == null) return null; 2223 TSQLSchema s = searchImplicitSchema(); 2224 if (s != null){ 2225 TSQLCatalog c = s.getCatalog(); 2226 if (c != null){ 2227 implictDatabase = c.getName(); 2228 } 2229 } 2230 2231 return implictDatabase; 2232 } 2233 2234 protected TSQLSchema searchImplicitSchema(){ 2235 TSQLSchema s = null; 2236 if (sqlEnv == null) return null; 2237 switch (dbObjectType){ 2238 case table: 2239 case view: 2240 TSQLTable t = sqlEnv.searchTable(".."+this.getObjectString()); 2241 if (t != null){ 2242 s = t.getSchema(); 2243 } 2244 2245 break; 2246 case function: 2247 case procedure: 2248 TSQLFunction f = sqlEnv.searchFunction(".."+this.getObjectString()); 2249 if (f != null){ 2250 s = f.getSchema(); 2251 } 2252 break; 2253 default: 2254 break; 2255 } 2256 2257 return s; 2258 } 2259 2260 /** 2261 * The table name of this objectName, it's the same value as {@link #getObjectToken} if {@link #getDbObjectType} 2262 * is {@link gudusoft.gsqlparser.EDbObjectType#table} 2263 * 2264 * @return table name 2265 */ 2266 public TSourceToken getTableToken(){ 2267 if (objectToken == null) return null; 2268 else return objectToken; 2269 } 2270 2271 /** 2272 * String text of the table name 2273 * 2274 * @return string of the table name, return null if empty. 2275 */ 2276 public String getTableString(){ 2277 if (objectToken == null) return ""; 2278// else if (!((dbObjectType == EDbObjectType.table)||(dbObjectType == EDbObjectType.view))){ 2279// return ""; 2280// } 2281 else return objectToken.toString(); 2282 } 2283 2284 /** 2285 * String text of the object name 2286 * 2287 * @return string of the object name, return null if empty. 2288 */ 2289 public String getObjectString() { 2290 if (objectToken != null) 2291 return objectToken.getAstext(); 2292 else 2293 return "" ; 2294 } 2295 2296 /** 2297 * String text of the part name 2298 * 2299 * @return string of the part name, return null if empty. 2300 */ 2301 public String getPartString() { 2302 if (partToken != null) 2303 return partToken.getAstext(); 2304 else 2305 return "" ; 2306 } 2307 2308 // ===== Phase 5: Cached normalized getters (reduce repeated normalize() calls) ===== 2309 2310 private transient gudusoft.gsqlparser.sqlenv.IdentifierService identifierService; 2311 2312 /** 2313 * Lazy initialization of IdentifierService for normalized identifier caching 2314 */ 2315 private gudusoft.gsqlparser.sqlenv.IdentifierService getIdentifierService() { 2316 if (identifierService == null && sqlEnv != null) { 2317 gudusoft.gsqlparser.sqlenv.IdentifierProfile profile = gudusoft.gsqlparser.sqlenv.IdentifierProfile.forVendor( 2318 sqlEnv.getDBVendor(), 2319 gudusoft.gsqlparser.sqlenv.IdentifierProfile.VendorFlags.defaults() 2320 ); 2321 identifierService = new gudusoft.gsqlparser.sqlenv.IdentifierService(profile, null); 2322 } 2323 return identifierService; 2324 } 2325 2326 /** 2327 * Get normalized database string with caching (Phase 5 optimization). 2328 * 2329 * <p>This method caches the normalized database name to avoid repeated normalize() calls. 2330 * The cache is invalidated automatically when the IdentifierProfile changes (e.g., vendor switch). 2331 * 2332 * @return normalized database name, or empty string if not available 2333 */ 2334 public String getNormalizedDatabaseString() { 2335 if (sqlEnv == null) return getDatabaseString(); 2336 2337 try { 2338 gudusoft.gsqlparser.sqlenv.IdentifierService service = getIdentifierService(); 2339 if (service == null) return getDatabaseString(); 2340 2341 long currentFingerprint = service.getProfile().getFingerprint(); 2342 2343 // Check if cache is valid (not null and fingerprint matches) 2344 if (normalizedDatabase == null || cacheFingerprint != currentFingerprint) { 2345 String raw = getDatabaseString(); 2346 if (raw != null && !raw.isEmpty()) { 2347 normalizedDatabase = service.normalize(raw, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType.dotCatalog); 2348 } else { 2349 normalizedDatabase = ""; 2350 } 2351 cacheFingerprint = currentFingerprint; 2352 } 2353 2354 return normalizedDatabase; 2355 } catch (Throwable t) { 2356 // Fallback to non-cached on any error 2357 return getDatabaseString(); 2358 } 2359 } 2360 2361 /** 2362 * Get normalized schema string with caching (Phase 5 optimization). 2363 * 2364 * <p>This method caches the normalized schema name to avoid repeated normalize() calls. 2365 * The cache is invalidated automatically when the IdentifierProfile changes. 2366 * 2367 * @return normalized schema name, or empty string if not available 2368 */ 2369 public String getNormalizedSchemaString() { 2370 if (sqlEnv == null) return getSchemaString(); 2371 2372 try { 2373 gudusoft.gsqlparser.sqlenv.IdentifierService service = getIdentifierService(); 2374 if (service == null) return getSchemaString(); 2375 2376 long currentFingerprint = service.getProfile().getFingerprint(); 2377 2378 // Check if cache is valid 2379 if (normalizedSchema == null || cacheFingerprint != currentFingerprint) { 2380 String raw = getSchemaString(); 2381 if (raw != null && !raw.isEmpty()) { 2382 normalizedSchema = service.normalize(raw, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType.dotSchema); 2383 } else { 2384 normalizedSchema = ""; 2385 } 2386 cacheFingerprint = currentFingerprint; 2387 } 2388 2389 return normalizedSchema; 2390 } catch (Throwable t) { 2391 // Fallback to non-cached 2392 return getSchemaString(); 2393 } 2394 } 2395 2396 /** 2397 * Get normalized table string with caching (Phase 5 optimization). 2398 * 2399 * <p>This method caches the normalized table name to avoid repeated normalize() calls. 2400 * The cache is invalidated automatically when the IdentifierProfile changes. 2401 * 2402 * @return normalized table name, or empty string if not available 2403 */ 2404 public String getNormalizedTableString() { 2405 if (sqlEnv == null) return getTableString(); 2406 2407 try { 2408 gudusoft.gsqlparser.sqlenv.IdentifierService service = getIdentifierService(); 2409 if (service == null) return getTableString(); 2410 2411 long currentFingerprint = service.getProfile().getFingerprint(); 2412 2413 // Check if cache is valid 2414 if (normalizedTable == null || cacheFingerprint != currentFingerprint) { 2415 String raw = getTableString(); 2416 if (raw != null && !raw.isEmpty()) { 2417 normalizedTable = service.normalize(raw, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType.dotTable); 2418 } else { 2419 normalizedTable = ""; 2420 } 2421 cacheFingerprint = currentFingerprint; 2422 } 2423 2424 return normalizedTable; 2425 } catch (Throwable t) { 2426 // Fallback to non-cached 2427 return getTableString(); 2428 } 2429 } 2430 2431 /** 2432 * Get normalized column string with caching (Phase 5 optimization). 2433 * 2434 * <p>This method caches the normalized column name (from partToken) to avoid repeated normalize() calls. 2435 * The cache is invalidated automatically when the IdentifierProfile changes. 2436 * 2437 * @return normalized column name, or empty string if not available 2438 */ 2439 public String getNormalizedColumnString() { 2440 if (sqlEnv == null) return getPartString(); 2441 2442 try { 2443 gudusoft.gsqlparser.sqlenv.IdentifierService service = getIdentifierService(); 2444 if (service == null) return getPartString(); 2445 2446 long currentFingerprint = service.getProfile().getFingerprint(); 2447 2448 // Check if cache is valid 2449 if (normalizedColumn == null || cacheFingerprint != currentFingerprint) { 2450 String raw = getPartString(); 2451 if (raw != null && !raw.isEmpty()) { 2452 normalizedColumn = service.normalize(raw, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType.dotColumn); 2453 } else { 2454 normalizedColumn = ""; 2455 } 2456 cacheFingerprint = currentFingerprint; 2457 } 2458 2459 return normalizedColumn; 2460 } catch (Throwable t) { 2461 // Fallback to non-cached 2462 return getPartString(); 2463 } 2464 } 2465 2466 /** 2467 * Get normalized server string with caching (Phase 5 optimization). 2468 * 2469 * <p>This method caches the normalized server name to avoid repeated normalize() calls. 2470 * The cache is invalidated automatically when the IdentifierProfile changes. 2471 * 2472 * @return normalized server name, or empty string if not available 2473 */ 2474 public String getNormalizedServerString() { 2475 if (sqlEnv == null) return getServerString(); 2476 2477 try { 2478 gudusoft.gsqlparser.sqlenv.IdentifierService service = getIdentifierService(); 2479 if (service == null) return getServerString(); 2480 2481 long currentFingerprint = service.getProfile().getFingerprint(); 2482 2483 // Check if cache is valid 2484 if (normalizedServer == null || cacheFingerprint != currentFingerprint) { 2485 String raw = getServerString(); 2486 if (raw != null && !raw.isEmpty()) { 2487 // Use dotUnknown for server since there's no dotServer type 2488 normalizedServer = service.normalize(raw, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType.dotUnknown); 2489 } else { 2490 normalizedServer = ""; 2491 } 2492 cacheFingerprint = currentFingerprint; 2493 } 2494 2495 return normalizedServer; 2496 } catch (Throwable t) { 2497 // Fallback to non-cached 2498 return getServerString(); 2499 } 2500 } 2501 2502 2503 public void setExclamationmark(TSourceToken exclamationmark) { 2504 this.exclamationmark = exclamationmark; 2505 } 2506 2507 private TSourceToken exclamationmark; // objectToken@!, ! is dblink 2508 2509 private Boolean isParsed = false; 2510 2511 private void parseObjectName(){ 2512 parseTablename(); 2513 } 2514 2515 private void parseTablename(){ 2516 if ((this.dbObjectType == EDbObjectType.variable) ||(this.dbObjectType == EDbObjectType.stage))return; 2517 2518 switch (this.dbvendor){ 2519 case dbvteradata: 2520 case dbvhive: 2521 if (objectToken != null){ 2522 databaseToken = objectToken; 2523 //databaseToken.setDbObjType(TObjectName.ttobjDatabaseName); 2524 databaseToken.setDbObjectType(EDbObjectType.database); 2525 schemaToken = objectToken; 2526 } 2527 objectToken = partToken; 2528 partToken = null; 2529 2530 break; 2531 default: 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 schemaToken = objectToken; 2546 schemaToken.setDbObjType(TObjectName.ttobjSchemaName); 2547 } 2548 2549 objectToken = partToken; 2550 partToken = null; 2551 break; 2552 } 2553 2554 if (objectToken != null){ 2555 objectToken.setDbObjectType(this.dbObjectType); 2556 } 2557 } 2558 2559 private void parseVariableName(){ 2560 if (databaseToken != null){ 2561 serverToken = databaseToken; 2562 //serverToken.setDbObjType(TObjectName.ttobjServerName); 2563 serverToken.setDbObjectType(EDbObjectType.server); 2564 } 2565 2566 if (schemaToken != null){ 2567 databaseToken = schemaToken; 2568 // databaseToken.setDbObjType(TObjectName.ttobjDatabaseName); 2569 databaseToken.setDbObjectType(EDbObjectType.database); 2570 } 2571 2572 if (objectToken != null){ 2573 if (partToken != null){ 2574 schemaToken = objectToken; 2575 schemaToken.setDbObjType(TObjectName.ttobjSchemaName); 2576 objectToken = partToken; 2577 partToken = null; 2578 }else{ 2579 2580 } 2581 }else{ 2582 objectToken = partToken; 2583 partToken = null; 2584 } 2585 } 2586 2587 private String ansiSchemaName; 2588 private String ansiCatalogName; 2589 2590 2591 /** 2592 * In this SQL: select * from part1.part2, 2593 * In Hive, MySQL and Teradata, part1 will be treated as a database name, returned in getDatabaseString(), 2594 * while getSchemaString() return empty string. 2595 * 2596 * However, TObjectName.getAnsiSchemaName() will return part1, which means it's a schema name. 2597 * 2598 * If a table name is not qualified with a schema name, but GSP detect the schema for this table in the metadata 2599 * then, this method will return this detected schema name. 2600 * 2601 * @return schema name 2602 */ 2603 public String getAnsiSchemaName(){ 2604 String ret = this.getSchemaString(); 2605 if ((ret.length() == 0) && ((this.getImplictSchemaString() != null) && (!this.getImplictSchemaString().equalsIgnoreCase("default")))){ 2606 ret = this.getImplictSchemaString(); 2607 } 2608 2609 switch (dbvendor){ 2610 case dbvmysql: 2611 case dbvhive: 2612 case dbvteradata: 2613 case dbvimpala: 2614 ret = this.getDatabaseString(); 2615 break; 2616 } 2617 return ret; 2618 } 2619 2620 /** 2621 * If a table name is not qualified with a database name, but GSP detect the database for this table in the metadata 2622 * then, this method will return this detected database name. 2623 * 2624 * @return 2625 */ 2626 public String getAnsiCatalogName(){ 2627 String ret = this.getDatabaseString(); 2628 if (( ret.length() == 0) && (this.getImplictDatabaseString() != null) && (!this.getImplictDatabaseString().equalsIgnoreCase("default"))){ 2629 ret = this.getImplictDatabaseString(); 2630 } 2631 2632 switch (dbvendor){ 2633 case dbvmysql: 2634 case dbvhive: 2635 case dbvteradata: 2636 case dbvimpala: 2637 ret = ""; 2638 break; 2639 } 2640 2641 return ret; 2642 } 2643 2644 private void parseFunctionName(){ 2645 this.parseTablename(); 2646 } 2647 2648 private void parseColumnMethodName(){ 2649 // objectType = ttobjColumnMethod; 2650 2651 methodToken = objectToken; 2652 partToken = schemaToken; 2653 2654 objectToken = null;//; 2655 schemaToken = null; 2656 } 2657 2658 private void parseColumnName(){ 2659 assert(partToken != null); 2660 } 2661 2662 public TObjectName(){ 2663 } 2664 2665 /** 2666 * List the number of parts made up this objectName 2667 * 2668 * @return the number of parts that made up this objectName 2669 */ 2670 public int getNumberOfPart() { 2671 return numberOfPart; 2672 } 2673 2674 private int numberOfPart = 1; 2675 2676 public static TObjectName createObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType){ 2677 return new TObjectName(dbVendor,dbObjectType); 2678 } 2679 2680 2681 public static TObjectName createObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token1){ 2682 return new TObjectName(dbVendor,dbObjectType,token1); 2683 } 2684 2685 public static TObjectName createObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType, String str) { 2686 String[] parts = str.split("\\."); 2687 if (parts.length == 1) { 2688 return new TObjectName(dbVendor, dbObjectType, new TSourceToken(parts[0])); 2689 } else if (parts.length == 2) { 2690 return new TObjectName(dbVendor, dbObjectType, new TSourceToken(parts[0]), new TSourceToken(parts[1])); 2691 } else if (parts.length == 3) { 2692 return new TObjectName(dbVendor, dbObjectType, new TSourceToken(parts[0]), new TSourceToken(parts[1]), new TSourceToken(parts[2])); 2693 } else if (parts.length == 4) { 2694 return new TObjectName(dbVendor, dbObjectType, new TSourceToken(parts[0]), new TSourceToken(parts[1]), new TSourceToken(parts[2]), new TSourceToken(parts[3])); 2695 } 2696 return new TObjectName(dbVendor, dbObjectType, new TSourceToken(str)); 2697 } 2698 2699 2700 public static TObjectName createObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token1,TSourceToken token2){ 2701 return new TObjectName(dbVendor,dbObjectType,token1,token2); 2702 } 2703 2704 public static TObjectName createObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token1,TSourceToken token2,TSourceToken token3){ 2705 return new TObjectName(dbVendor,dbObjectType,token1,token2,token3); 2706 } 2707 2708 public static TObjectName createObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token1,TSourceToken token2,TSourceToken token3,TSourceToken token4){ 2709 return new TObjectName(dbVendor,dbObjectType,token1,token2,token3,token4); 2710 } 2711 2712 /** 2713 * @deprecated As of v2.0.7.1, please use {@link #TObjectName(EDbObjectType, TSourceToken)} instead. 2714 * 2715 * Class constructor specifying object name and object type. 2716 * <p> 2717 * Use {@link gudusoft.gsqlparser.TGSqlParser#parseObjectName} to create an objectName more than 2 parts. 2718 * 2719 * @param token name of this object 2720 * @param dbObjectType type of this object 2721 */ 2722 private TObjectName(TSourceToken token,EDbObjectType dbObjectType){ 2723 this(dbObjectType,token); 2724 } 2725 2726 public void splitNameInQuotedIdentifier(){ 2727 if (this.dbvendor != EDbVendor.dbvbigquery && this.dbvendor != EDbVendor.dbvsnowflake) return; 2728 if (this.objectToken == null) return; 2729 TSourceToken token = this.objectToken; 2730 2731 // For Snowflake IDENTIFIER function, the token is a single-quoted string literal 2732 // which has quoteType notQuoted but starts with single quote 2733 boolean isSnowflakeSingleQuotedString = (this.dbvendor == EDbVendor.dbvsnowflake) 2734 && token.toString().startsWith("'"); 2735 2736 if (getQuoteType() == EQuoteType.notQuoted && !isSnowflakeSingleQuotedString) return; 2737// if ((this.dbvendor != EDbVendor.dbvbigquery) 2738// &&(getQuoteType() == EQuoteType.doubleQuote)) return; 2739 2740 String tokenStr = token.toString(); 2741 char outerQuoteChar = tokenStr.charAt(0); 2742 String s = TBaseType.getTextWithoutQuoted(tokenStr); 2743 String[] a = s.split("[.]"); 2744 if (a.length == 1){ 2745 // this.objectToken = token; 2746 }else if (a.length == 2){ 2747 String objPart = a[1]; 2748 String schemaPart = a[0]; 2749 2750 // For Snowflake IDENTIFIER function with single-quoted string containing double-quoted parts 2751 // e.g., IDENTIFIER('"SCHEMA"."TABLE"') -> parts already have double quotes 2752 // For unquoted parts, e.g., IDENTIFIER('schema.table') -> use parts as-is 2753 boolean isSnowflakeIdentifierFunction = (this.dbvendor == EDbVendor.dbvsnowflake) && (outerQuoteChar == '\''); 2754 2755 if (isSnowflakeIdentifierFunction) { 2756 this.objectToken = new TSourceToken(objPart); 2757 this.schemaToken = new TSourceToken(schemaPart); 2758 } else { 2759 this.objectToken = new TSourceToken(outerQuoteChar + objPart + outerQuoteChar); 2760 this.schemaToken = new TSourceToken(outerQuoteChar + schemaPart + outerQuoteChar); 2761 } 2762 }else if (a.length == 3){ 2763 String objPart = a[2]; 2764 String schemaPart = a[1]; 2765 String dbPart = a[0]; 2766 2767 boolean isSnowflakeIdentifierFunction = (this.dbvendor == EDbVendor.dbvsnowflake) && (outerQuoteChar == '\''); 2768 2769 if (isSnowflakeIdentifierFunction) { 2770 this.objectToken = new TSourceToken(objPart); 2771 this.schemaToken = new TSourceToken(schemaPart); 2772 this.databaseToken = new TSourceToken(dbPart); 2773 } else { 2774 this.objectToken = new TSourceToken(outerQuoteChar + objPart + outerQuoteChar); 2775 this.schemaToken = new TSourceToken(outerQuoteChar + schemaPart + outerQuoteChar); 2776 this.databaseToken = new TSourceToken(outerQuoteChar + dbPart + outerQuoteChar); 2777 } 2778 } 2779 2780 } 2781 2782 private TObjectName(EDbVendor dbVendor){ 2783 this.dbvendor = dbVendor; 2784 } 2785 2786 private TObjectName(EDbVendor dbVendor,EDbObjectType dbObjectType){ 2787 this.dbvendor = dbVendor; 2788 this.dbObjectType = dbObjectType; 2789 } 2790 2791 private TObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token){ 2792 this.dbvendor = dbVendor; 2793 numberOfPart = 1; 2794 this.setStartToken(token); 2795 this.setEndToken(token); 2796 2797 this.dbObjectType = dbObjectType; 2798 switch (dbObjectType){ 2799 case column: 2800 this.partToken = token; 2801 break; 2802 case method: 2803 this.methodToken = token; 2804 break; 2805 case table: 2806 case function: 2807 case procedure: 2808 case materializedView: 2809 case alias: 2810 case module: 2811 case sequence: 2812 case collation: 2813 this.objectToken = token; 2814 splitNameInQuotedIdentifier(); 2815 break; 2816 default: 2817 this.objectToken = token; 2818 break; 2819 } 2820 } 2821 2822 private void initWithOneToken(EDbObjectType dbObjectType,TSourceToken token){ 2823 numberOfPart = 1; 2824 this.setStartToken(token); 2825 this.setEndToken(token); 2826 2827 this.dbObjectType = dbObjectType; 2828 switch (dbObjectType){ 2829 case column: 2830 this.partToken = token; 2831 break; 2832 case method: 2833 this.methodToken = token; 2834 break; 2835 case table: 2836 case function: 2837 case procedure: 2838 case materializedView: 2839 case alias: 2840 case module: 2841 case sequence: 2842 case collation: 2843 case stage: 2844 this.objectToken = token; 2845 splitNameInQuotedIdentifier(); 2846 break; 2847 case namespace: 2848 this.schemaToken = token; 2849 break; 2850 default: 2851 this.objectToken = token; 2852 break; 2853 } 2854 } 2855 2856 private void initWithTwoTokens(EDbObjectType dbObjectType,TSourceToken token2,TSourceToken token1){ 2857 initWithOneToken(dbObjectType,token1); 2858 numberOfPart = 2; 2859 this.setStartToken(token2); 2860 this.setEndToken(token1); 2861 2862 switch (dbObjectType){ 2863 case column: 2864 this.objectToken = token2; 2865 break; 2866 case method: 2867 this.partToken = token2; 2868 break; 2869 case table: 2870 case function: 2871 case procedure: 2872 case materializedView: 2873 case alias: 2874 case module: 2875 case sequence: 2876 case collation: 2877 case stage: 2878 this.schemaToken = token2; 2879 break; 2880 case namespace: 2881 this.databaseToken = token2; 2882 break; 2883 default: 2884 this.schemaToken = token2; 2885 break; 2886 } 2887 2888 } 2889 2890 private void initWithThreeTokens(EDbObjectType dbObjectType,TSourceToken token3,TSourceToken token2,TSourceToken token1){ 2891 initWithTwoTokens(dbObjectType,token2,token1); 2892 numberOfPart = 3; 2893 this.setStartToken(token3); 2894 this.setEndToken(token1); 2895 2896 switch (dbObjectType){ 2897 case column: 2898 this.schemaToken = token3; 2899 break; 2900 case method: 2901 this.objectToken = token3; 2902 break; 2903 case table: 2904 case function: 2905 case procedure: 2906 case materializedView: 2907 case alias: 2908 case module: 2909 case sequence: 2910 case collation: 2911 case stage: 2912 this.databaseToken = token3; 2913 break; 2914 default: 2915 this.databaseToken = token3; 2916 break; 2917 } 2918 2919 } 2920 2921 private void initWithFourTokens(EDbObjectType dbObjectType,TSourceToken token4,TSourceToken token3,TSourceToken token2,TSourceToken token1){ 2922 initWithThreeTokens(dbObjectType,token3,token2,token1); 2923 numberOfPart = 4; 2924 this.setStartToken(token4); 2925 this.setEndToken(token1); 2926 2927 switch (dbObjectType){ 2928 case column: 2929 this.databaseToken = token4; 2930 break; 2931 case method: 2932 this.schemaToken = token4; 2933 break; 2934 case table: 2935 case function: 2936 case procedure: 2937 case materializedView: 2938 case alias: 2939 case module: 2940 case sequence: 2941 case collation: 2942 this.serverToken = token4; 2943 break; 2944 default: 2945 this.serverToken = token4; 2946 break; 2947 } 2948 2949 } 2950 2951 /** 2952 * @deprecated As of v2.0.7.1, please use {@link TObjectName#createObjectName(EDbVendor, EDbObjectType, TSourceToken)} instead. 2953 * 2954 * @param dbObjectType 2955 * @param token 2956 */ 2957 private TObjectName(EDbObjectType dbObjectType,TSourceToken token){ 2958 initWithOneToken(dbObjectType,token); 2959 } 2960 2961 private TObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token2,TSourceToken token1){ 2962 this(dbVendor,dbObjectType,token1); 2963 numberOfPart = 2; 2964 this.setStartToken(token2); 2965 this.setEndToken(token1); 2966 2967 switch (dbObjectType){ 2968 case column: 2969 this.objectToken = token2; 2970 break; 2971 case method: 2972 this.partToken = token2; 2973 break; 2974 case table: 2975 case function: 2976 case procedure: 2977 case materializedView: 2978 case alias: 2979 case module: 2980 case sequence: 2981 case collation: 2982 if (dbVendor == EDbVendor.dbvteradata){ 2983 this.databaseToken = token2; 2984 }else{ 2985 this.schemaToken = token2; 2986 } 2987 2988 break; 2989 default: 2990 this.schemaToken = token2; 2991 break; 2992 } 2993 2994 } 2995 2996 2997 /** 2998 * @deprecated since ver 2.5.9.8 2999 * 3000 * @param dbObjectType 3001 * @param token2 3002 * @param token1 3003 */ 3004 private TObjectName(EDbObjectType dbObjectType,TSourceToken token2,TSourceToken token1){ 3005 this(dbObjectType,token1); 3006 numberOfPart = 2; 3007 this.setStartToken(token2); 3008 this.setEndToken(token1); 3009 3010 switch (dbObjectType){ 3011 case column: 3012 this.objectToken = token2; 3013 break; 3014 case method: 3015 this.partToken = token2; 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.schemaToken = token2; 3026 break; 3027 default: 3028 this.schemaToken = token2; 3029 break; 3030 } 3031 } 3032 private TObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token3,TSourceToken token2,TSourceToken token1){ 3033 this(dbVendor,dbObjectType,token2,token1); 3034 numberOfPart = 3; 3035 this.setStartToken(token3); 3036 this.setEndToken(token1); 3037 3038 switch (dbObjectType){ 3039 case column: 3040 this.schemaToken = token3; 3041 break; 3042 case method: 3043 this.objectToken = token3; 3044 break; 3045 case table: 3046 case function: 3047 case procedure: 3048 case materializedView: 3049 case alias: 3050 case module: 3051 case sequence: 3052 case collation: 3053 this.databaseToken = token3; 3054 break; 3055 default: 3056 this.databaseToken = token3; 3057 break; 3058 } 3059 3060 } 3061 3062 3063 /** 3064 * @deprecated since ver 2.5.9.8 3065 * 3066 * @param dbObjectType 3067 * @param token3 3068 * @param token2 3069 * @param token1 3070 */ 3071 private TObjectName(EDbObjectType dbObjectType,TSourceToken token3,TSourceToken token2,TSourceToken token1){ 3072 this(dbObjectType,token2,token1); 3073 numberOfPart = 3; 3074 this.setStartToken(token3); 3075 this.setEndToken(token1); 3076 3077 switch (dbObjectType){ 3078 case column: 3079 this.schemaToken = token3; 3080 break; 3081 case method: 3082 this.objectToken = token3; 3083 break; 3084 case table: 3085 case function: 3086 case procedure: 3087 case materializedView: 3088 case alias: 3089 case module: 3090 case sequence: 3091 case collation: 3092 this.databaseToken = token3; 3093 break; 3094 default: 3095 this.databaseToken = token3; 3096 break; 3097 } 3098 } 3099 3100 private TObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token4,TSourceToken token3,TSourceToken token2,TSourceToken token1){ 3101 this(dbVendor,dbObjectType,token3,token2,token1); 3102 numberOfPart = 4; 3103 this.setStartToken(token4); 3104 this.setEndToken(token1); 3105 3106 switch (dbObjectType){ 3107 case column: 3108 this.databaseToken = token4; 3109 break; 3110 case method: 3111 this.schemaToken = token4; 3112 break; 3113 case table: 3114 case function: 3115 case procedure: 3116 case materializedView: 3117 case alias: 3118 case module: 3119 case sequence: 3120 case collation: 3121 this.serverToken = token4; 3122 break; 3123 default: 3124 this.serverToken = token4; 3125 break; 3126 } 3127 3128 } 3129 3130 /** 3131 * @deprecated since ver 2.5.9.8 3132 * 3133 * @param dbObjectType 3134 * @param token4 3135 * @param token3 3136 * @param token2 3137 * @param token1 3138 */ 3139 private TObjectName(EDbObjectType dbObjectType,TSourceToken token4,TSourceToken token3,TSourceToken token2,TSourceToken token1){ 3140 this(dbObjectType,token3,token2,token1); 3141 numberOfPart = 4; 3142 this.setStartToken(token4); 3143 this.setEndToken(token1); 3144 3145 switch (dbObjectType){ 3146 case column: 3147 this.databaseToken = token4; 3148 break; 3149 case method: 3150 this.schemaToken = token4; 3151 break; 3152 case table: 3153 case function: 3154 case procedure: 3155 case materializedView: 3156 case alias: 3157 case module: 3158 case sequence: 3159 case collation: 3160 this.serverToken = token4; 3161 break; 3162 default: 3163 this.serverToken = token4; 3164 break; 3165 } 3166 } 3167 3168 /** 3169 * @deprecated As of v2.0.7.1, please use {@link #TObjectName(EDbObjectType, TSourceToken, TSourceToken)} instead. 3170 * 3171 * Class constructor specifying object, part name and object type. 3172 * Use this constructor to create a <code>table.column</code> objectName. 3173 * Use {@link gudusoft.gsqlparser.TGSqlParser#parseObjectName} to create an objectName more than 2 parts. 3174 * 3175 * @param pObjectToken name of this object, usually it's the table name 3176 * @param pPartToken name of the column 3177 * @param dbObjectType type of this object, usually it's {@link gudusoft.gsqlparser.EDbObjectType#column} 3178 */ 3179 private TObjectName(TSourceToken pObjectToken,TSourceToken pPartToken,EDbObjectType dbObjectType){ 3180 this(dbObjectType,pObjectToken,pPartToken); 3181 } 3182 3183 public void init(Object arg1) 3184 { 3185 partToken = (TSourceToken)arg1; 3186 numberOfPart = 1; 3187 this.setStartToken(partToken); 3188 this.setEndToken(partToken); 3189 } 3190 3191 public void init(Object arg1, Object arg2) 3192 { 3193 if (arg1 instanceof EDbObjectType){ 3194 initWithOneToken((EDbObjectType)arg1,(TSourceToken) arg2); 3195 3196 }else{ 3197 numberOfPart = 0; 3198 objectToken = (TSourceToken)arg1; 3199 partToken = (TSourceToken)arg2; 3200 if (partToken != null) numberOfPart++; 3201 if (objectToken != null) numberOfPart++; 3202 3203 3204 if(objectToken != null){ 3205 this.setStartToken(objectToken); 3206 }else{ 3207 this.setStartToken(partToken); 3208 } 3209 3210 if (partToken != null){ 3211 this.setEndToken(partToken); 3212 }else{ 3213 this.setEndToken(objectToken); 3214 } 3215 } 3216 } 3217 3218 public void init(EDbObjectType dbObjectType, Object arg1, Object arg2, Object arg3){ 3219 numberOfPart = 0; 3220 if (arg1 != null) numberOfPart++; 3221 if (arg2 != null) numberOfPart++; 3222 if (arg3 != null) numberOfPart++; 3223 3224 this.dbObjectType = dbObjectType; 3225 this.setStartToken((TSourceToken)arg1); 3226 this.setEndToken((TSourceToken)arg3); 3227 switch (this.dbObjectType){ 3228 case column: 3229 schemaToken = (TSourceToken)arg1; 3230 objectToken = (TSourceToken)arg2; 3231 partToken = (TSourceToken)arg3; 3232 break; 3233 case table: 3234 case function: 3235 case procedure: 3236 case materializedView: 3237 case module: 3238 case sequence: 3239 databaseToken = (TSourceToken) arg1; 3240 schemaToken = (TSourceToken)arg2; 3241 objectToken = (TSourceToken)arg3; 3242 break; 3243 case alias: 3244 break; 3245 default: 3246 break; 3247 } 3248 3249 } 3250 3251 public void init(Object arg1, Object arg2, Object arg3) 3252 { 3253 if (arg1 instanceof EDbObjectType){ 3254 initWithTwoTokens((EDbObjectType)arg1,(TSourceToken) arg2,(TSourceToken) arg3); 3255 }else{ 3256 numberOfPart = 0; 3257 if (arg1 != null) numberOfPart++; 3258 if (arg2 != null) numberOfPart++; 3259 if (arg3 != null) numberOfPart++; 3260 3261 if (dbvendor == EDbVendor.dbvteradata){ 3262 databaseToken = (TSourceToken) arg1; 3263 this.setStartToken(databaseToken); 3264 }else{ 3265 schemaToken = (TSourceToken)arg1; 3266 this.setStartToken(schemaToken); 3267 if (schemaToken != null) 3268 {schemaToken.setDbObjType(TObjectName.ttobjSchemaName);} 3269 } 3270 3271 objectToken = (TSourceToken)arg2; 3272 partToken = (TSourceToken)arg3; 3273 this.setEndToken(partToken); 3274 } 3275 } 3276 3277 public void init(Object arg1, Object arg2, Object arg3, Object arg4) 3278 { 3279 if (arg1 instanceof EDbObjectType){ 3280 //this.dbObjectType = (EDbObjectType)arg1; 3281 //init(arg2,arg3,arg4); 3282 initWithThreeTokens((EDbObjectType)arg1,(TSourceToken)arg2,(TSourceToken)arg3,(TSourceToken)arg4); 3283 }else{ 3284 numberOfPart = 0; 3285 if (arg1 != null) numberOfPart++; 3286 if (arg2 != null) numberOfPart++; 3287 if (arg3 != null) numberOfPart++; 3288 if (arg4 != null) numberOfPart++; 3289 3290 //serverToken = (TSourceToken)arg1; 3291 databaseToken = (TSourceToken)arg1; 3292 schemaToken = (TSourceToken)arg2; 3293 objectToken = (TSourceToken)arg3; 3294 partToken = (TSourceToken)arg4; 3295 this.setStartToken(databaseToken); 3296 this.setEndToken(partToken); 3297 if (databaseToken != null){ 3298 //databaseToken.setDbObjType(TObjectName.ttobjDatabaseName); 3299 databaseToken.setDbObjectType(EDbObjectType.database); 3300 }else { 3301 } 3302 if (schemaToken != null){ 3303 schemaToken.setDbObjType(TObjectName.ttobjSchemaName); 3304 }else { 3305 } 3306 } 3307 } 3308 3309 public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) 3310 { 3311 numberOfPart = 0; 3312 if (arg1 != null) numberOfPart++; 3313 if (arg2 != null) numberOfPart++; 3314 if (arg3 != null) numberOfPart++; 3315 if (arg4 != null) numberOfPart++; 3316 if (arg5 != null) numberOfPart++; 3317 3318 serverToken = (TSourceToken)arg1; 3319 databaseToken = (TSourceToken)arg2; 3320 schemaToken = (TSourceToken)arg3; 3321 objectToken = (TSourceToken)arg4; 3322 partToken = (TSourceToken)arg5; 3323 3324 this.setStartToken(serverToken); 3325 this.setEndToken(partToken); 3326 3327 if (serverToken != null){ 3328 //serverToken.setDbObjType(TObjectName.ttobjServerName); 3329 serverToken.setDbObjectType(EDbObjectType.server); 3330 }else{ 3331 } 3332 if (databaseToken != null){ 3333 //databaseToken.setDbObjType(TObjectName.ttobjDatabaseName); 3334 databaseToken.setDbObjectType(EDbObjectType.database); 3335 }else{ 3336 } 3337 if (schemaToken != null){ 3338 schemaToken.setDbObjType(TObjectName.ttobjSchemaName); 3339 }else{ 3340 } 3341 } 3342 3343 public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) 3344 { 3345 numberOfPart = 0; 3346 if (arg1 != null) numberOfPart++; 3347 if (arg2 != null) numberOfPart++; 3348 if (arg3 != null) numberOfPart++; 3349 if (arg4 != null) numberOfPart++; 3350 if (arg5 != null) numberOfPart++; 3351 if (arg6 != null) numberOfPart++; 3352 3353 serverToken = (TSourceToken)arg1; 3354 databaseToken = (TSourceToken)arg2; 3355 schemaToken = (TSourceToken)arg3; 3356 objectToken = (TSourceToken)arg4; 3357 partToken = (TSourceToken)arg5; 3358 propertyToken = (TSourceToken)arg6; 3359 3360 this.setStartToken(serverToken); 3361 this.setEndToken(propertyToken); 3362 3363 if (serverToken != null){ 3364 //serverToken.setDbObjType(TObjectName.ttobjServerName); 3365 serverToken.setDbObjectType(EDbObjectType.server); 3366 }else{ 3367 } 3368 if (databaseToken != null){ 3369 // databaseToken.setDbObjType(TObjectName.ttobjDatabaseName); 3370 databaseToken.setDbObjectType(EDbObjectType.database); 3371 }else{ 3372 } 3373 if (schemaToken != null){ 3374 schemaToken.setDbObjType(TObjectName.ttobjSchemaName); 3375 }else{ 3376 } 3377 } 3378 3379 /** 3380 * Init with 7 tokens for deeply nested struct field access (e.g., BigQuery) 3381 * Pattern: a.b.c.d.e.f.g (7 parts) 3382 */ 3383 public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7) 3384 { 3385 // First 6 parts use standard tokens 3386 init(arg1, arg2, arg3, arg4, arg5, arg6); 3387 3388 // 7th part goes to additionalParts 3389 if (arg7 != null) { 3390 if (additionalParts == null) { 3391 additionalParts = new java.util.ArrayList<>(); 3392 } 3393 additionalParts.add((TSourceToken) arg7); 3394 numberOfPart++; 3395 this.setEndToken((TSourceToken) arg7); 3396 } 3397 } 3398 3399 /** 3400 * Init with 8 tokens for deeply nested struct field access 3401 * Pattern: a.b.c.d.e.f.g.h (8 parts) 3402 */ 3403 public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8) 3404 { 3405 init(arg1, arg2, arg3, arg4, arg5, arg6, arg7); 3406 3407 if (arg8 != null) { 3408 if (additionalParts == null) { 3409 additionalParts = new java.util.ArrayList<>(); 3410 } 3411 additionalParts.add((TSourceToken) arg8); 3412 numberOfPart++; 3413 this.setEndToken((TSourceToken) arg8); 3414 } 3415 } 3416 3417 /** 3418 * Init with 9 tokens for deeply nested struct field access 3419 * Pattern: a.b.c.d.e.f.g.h.i (9 parts) 3420 */ 3421 public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9) 3422 { 3423 init(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); 3424 3425 if (arg9 != null) { 3426 if (additionalParts == null) { 3427 additionalParts = new java.util.ArrayList<>(); 3428 } 3429 additionalParts.add((TSourceToken) arg9); 3430 numberOfPart++; 3431 this.setEndToken((TSourceToken) arg9); 3432 } 3433 } 3434 3435 /** 3436 * Init with 10 tokens for deeply nested struct field access 3437 * Pattern: a.b.c.d.e.f.g.h.i.j (10 parts) 3438 */ 3439 public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9, Object arg10) 3440 { 3441 init(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); 3442 3443 if (arg10 != null) { 3444 if (additionalParts == null) { 3445 additionalParts = new java.util.ArrayList<>(); 3446 } 3447 additionalParts.add((TSourceToken) arg10); 3448 numberOfPart++; 3449 this.setEndToken((TSourceToken) arg10); 3450 } 3451 } 3452 3453 /** 3454 * Get additional parts beyond the standard 6 tokens. 3455 * Used for deeply nested struct field access in databases like BigQuery. 3456 * 3457 * @return list of additional source tokens, or null if none 3458 */ 3459 public java.util.List<TSourceToken> getAdditionalParts() { 3460 return additionalParts; 3461 } 3462 3463 /** 3464 * The column position of this objectName in the SQL 3465 * 3466 * @return column position 3467 */ 3468 @Override 3469 public long getColumnNo() { 3470 long retval = -1; 3471 if (partToken != null) { retval = partToken.columnNo;} 3472 if (objectToken != null) { 3473 retval = objectToken.columnNo; 3474 } 3475 if (schemaToken != null) { 3476 retval = schemaToken.columnNo; 3477 } 3478 if (databaseToken != null) { 3479 retval = databaseToken.columnNo; 3480 } 3481 if (serverToken != null) { 3482 retval = serverToken.columnNo; 3483 } 3484 return retval; 3485 } 3486 3487 /** 3488 * The line number of this objectName in SQL 3489 * 3490 * @return the line number 3491 */ 3492 @Override 3493 public long getLineNo() { 3494 long retval = -1; 3495 if (partToken != null) { retval = partToken.lineNo;} 3496 if (objectToken != null) { 3497 retval = objectToken.lineNo; 3498 } 3499 if (schemaToken != null) { 3500 retval = schemaToken.lineNo; 3501 } 3502 if (databaseToken != null) { 3503 retval = databaseToken.lineNo; 3504 } 3505 if (serverToken != null) { 3506 retval = serverToken.lineNo; 3507 } 3508 return retval; 3509 } 3510 3511 private TObjectNameList referencedObjects = null; 3512 3513 public TObjectNameList getReferencedObjects() { 3514 if (referencedObjects == null){ 3515 referencedObjects = new TObjectNameList(); 3516 } 3517 return referencedObjects; 3518 } 3519 3520 /** 3521 * Returns only the column name if it's prefixed with a table name 3522 * 3523 * @return only the column name if it's prefixed with a table name 3524 */ 3525 public String getColumnNameOnly(){ 3526 3527 if (getPartToken() == null) return ""; 3528 else return getPartToken().toString(); 3529 } 3530 3531 public void accept(TParseTreeVisitor v){ 3532 v.preVisit(this); 3533 v.postVisit(this); 3534 } 3535 3536 public void acceptChildren(TParseTreeVisitor v){ 3537 v.preVisit(this); 3538 v.postVisit(this); 3539 } 3540 3541// private TSourceToken sortType = null; 3542// 3543// public void setSortType(TSourceToken sortType) { 3544// this.sortType = sortType; 3545// } 3546// 3547// /** 3548// * When this object is column in primary key(column,...), unique key(column,...) in sql server 3549// * there maybe sort information like column asc, column desc 3550// * this token represents for ASC, DESC if specified. 3551// * 3552// * @return ASC, DESC or null 3553// */ 3554// 3555// public TSourceToken getSortType() { 3556// return sortType; 3557// } 3558 3559 /** 3560 * It's the same as {@link #getPartToken} if {@link #getDbObjectType} is {@link gudusoft.gsqlparser.EDbObjectType#column} 3561 * 3562 * @return source token that represents column, return null if this objectName is not type of column 3563 * 3564 */ 3565 public TSourceToken getColumnToken(){ 3566 TSourceToken ret = null; 3567 if (this.getObjectType() == ttobjColumn){ 3568 ret = this.getPartToken(); 3569 } 3570 return ret; 3571 } 3572 3573 /* 3574 * re-arranage objectname to make it a valid name includes attribute name 3575 * used by teradata yacc file only. 3576 * valid syntax: 3577 * column.attr1().attr2(). 3578 * column.attr1().attr2().attr3() 3579 * table.column.attr1().attr2(). 3580 * table.column.attr1().attr2().attr3() 3581 * 3582 * @return 3583 */ 3584 public boolean isAttributeNameInObjectName(TSourceToken leftparen,TSourceToken rightparen){ 3585 boolean ret = false; 3586 if ((this.partToken == null) || (this.objectToken == null)){ 3587 return ret; 3588 } 3589 this.objectType = TObjectName.ttobjColumn; 3590 this.columnAttributes = new TObjectNameList(); 3591 TObjectName attr1 = new TObjectName(); 3592 attr1.objectType = TObjectName.ttobjAttribute; 3593 attr1.init(this.partToken); 3594 attr1.setEndToken(rightparen); 3595 this.columnAttributes.addObjectName(attr1); 3596 3597 this.partToken = this.objectToken; 3598 3599 if (this.schemaToken != null){ 3600 this.objectToken = this.schemaToken; 3601 } 3602 3603 return true; 3604 } 3605 3606 /** 3607 * Used internally in hive .y file to merge two objectNames 3608 */ 3609 public void mergeObjectName(TObjectName objectName){ 3610 this.objectToken = this.partToken; 3611 this.partToken = objectName.getPartToken(); 3612 this.setStartToken(objectToken); 3613 this.setEndToken(partToken); 3614 } 3615 3616 public void mergeObjectName(TObjectName objectName,TObjectName objectName2){ 3617 this.schemaToken = this.partToken; 3618 this.objectToken = objectName.getPartToken(); 3619 this.partToken = objectName2.getPartToken(); 3620 this.setStartToken(schemaToken); 3621 this.setEndToken(partToken); 3622 } 3623 3624 3625 public void columnToProperty(){ 3626 // if (numberOfPart == 1) return; 3627 if (this.pseudoTableType != EPseudoTableType.none) return; // pseudo table tokens already in correct position 3628 if (this.propertyToken != null) return; // columnToProperty() already called 3629 if (! ((this.partToken != null) && (this.objectToken != null))) return; // 既然是 column.property , 那么 partToken and objectToken 不能为空 3630 3631 this.propertyToken = this.partToken; 3632 this.partToken = this.objectToken; 3633 this.objectToken = this.schemaToken; 3634 3635 this.setDbObjectTypeDirectly(EDbObjectType.column); 3636 } 3637 3638 public void appendObjectName(TObjectName objectName){ 3639 if (this.databaseToken != null){ 3640 this.serverToken = this.databaseToken; 3641 } 3642 if (this.schemaToken != null){ 3643 this.databaseToken = this.schemaToken; 3644 } 3645 if (this.objectToken != null){ 3646 this.schemaToken = this.objectToken; 3647 } 3648 this.objectToken = this.partToken; 3649 this.partToken = objectName.getPartToken(); 3650 this.setEndTokenDirectly(this.partToken); 3651 } 3652 3653 private TSourceToken commentString; 3654 3655 public void setCommentString(TSourceToken commentString) { 3656 this.commentString = commentString; 3657 } 3658 3659 public TSourceToken getCommentString() { 3660 3661 return commentString; 3662 } 3663 3664 3665 /** 3666 * The X and Y position of this objectName in the SQL 3667 * 3668 * @return coordinate in string text 3669 */ 3670 public String coordinate(){ 3671 return this.getStartToken().lineNo+","+this.getEndToken().columnNo; 3672 } 3673 3674 3675 /** 3676 * @deprecated replaced by {@link EDbObjectType}. 3677 * 3678 * this is not an object, like sysdate function in oracle database 3679 */ 3680 public final static int ttobjNotAObject = -1; 3681 3682 /** 3683 * @deprecated replaced by {@link EDbObjectType}. 3684 * object type can't be determined. 3685 */ 3686 public final static int ttobjUnknown = 0; 3687 3688 /** 3689 * @deprecated replaced by {@link EDbObjectType}. 3690 * column in table, objectToken is table if specified, and partToken is column name. 3691 */ 3692 public final static int ttobjColumn = 1; 3693 3694 /** 3695 * @deprecated replaced by {@link EDbObjectType}. 3696 * column alias in objectToken. 3697 */ 3698 public final static int ttobjColumnAlias = 2; 3699 3700 /** 3701 * @deprecated replaced by {@link EDbObjectType}. 3702 * table name in objectToken. 3703 */ 3704 public final static int ttobjTable = 3; 3705 3706 3707 /** 3708 * @deprecated replaced by {@link EDbObjectType}. 3709 * parameter name in objectToken. 3710 */ 3711 public final static int ttobjParameter = 9; 3712 3713 /** 3714 * @deprecated replaced by {@link EDbObjectType}. 3715 * variable name in objectToken. 3716 */ 3717 public final static int ttobjVariable = 10; 3718 3719 3720 /** 3721 * @deprecated replaced by {@link EDbObjectType#method}. 3722 * column method like SetXY below, column method in {@link #methodToken}, and colomn name in {@link #partToken}. 3723 *<p> UPDATE Cities 3724 *<p> SET Location.SetXY(23.5, 23.5) 3725 * 3726 * 3727 */ 3728 public final static int ttobjColumnMethod = 11; 3729 3730 /** 3731 * Named argument parameter name in function calls. 3732 * <p>Example: In Snowflake FLATTEN(INPUT => parse_json(col), outer => TRUE), 3733 * "INPUT" and "outer" are named argument parameter names, NOT column references. 3734 * <p>These should be skipped during column resolution and data lineage analysis. 3735 */ 3736 public final static int ttobjNamedArgParameter = 12; 3737 3738 /** 3739 * @deprecated replaced by {@link EDbObjectType}. 3740 * function name in {@link #objectToken} 3741 */ 3742 public final static int ttobjFunctionName = 13; 3743 3744 3745 /** 3746 * @deprecated replaced by {@link EDbObjectType#constraint}. 3747 * constraint name in {@link #objectToken} 3748 */ 3749 public final static int ttobjConstraintName = 19; 3750 3751 /** 3752 * @deprecated replaced by {@link EDbObjectType}. 3753 * string constant in {@link #objectToken} 3754 */ 3755 public final static int ttobjStringConstant = 23; 3756 3757 3758 /** 3759 * @deprecated replaced by {@link EDbObjectType}. 3760 * attribute name is in {@link #partToken} 3761 */ 3762 public final static int ttobjAttribute = 26; 3763 3764 3765 /** 3766 * @deprecated replaced by {@link EDbObjectType}. 3767 * datatype was not represented by a TObjectName object, this constant was used in source tokens that consist of TTypeName. 3768 */ 3769 public final static int ttobjDatatype = 30; 3770 3771 /** 3772 * @deprecated replaced by {@link EDbObjectType}. 3773 * schema name in {@link #schemaToken} 3774 */ 3775 public final static int ttobjSchemaName = 31; 3776 3777 3778 /** 3779 * @deprecated replaced by {@link EDbObjectType}. 3780 * postgresql 3781 * Positional Parameters, $1, $1[1], $1[1,10] 3782 * parameter name is in {@link #partToken} of $1, 3783 * and parameter name is in {@link #objectToken} of $1.columnName, 3784 * and column name is in {@link #partToken} 3785 */ 3786 3787 public final static int ttobjPositionalParameters = 61; 3788 3789 3790 3791 private void setColumnTokenOfPositionalParameters(TSourceToken column){ 3792 this.objectToken = this.partToken; 3793 this.partToken = column; 3794 } 3795 3796 private int objectType = ttobjUnknown; 3797 3798 3799 /** 3800 * @deprecated replaced by {@link EDbObjectType}. 3801 * this type is used in TObjectNameList, when objects in TObjectNameList includes more than 3802 * one type, objtype of that TObjectNameList was set to ttobjMixed. 3803 * 3804 * removed since v2.9.2.5 3805 */ 3806 // public final static int ttobjMixed = 100; 3807 3808 /** 3809 * @deprecated replaced by {@link EDbObjectType#library}. 3810 * removed since v2.9.2.5 3811 */ 3812 // public final static int ttObjLibrary = 72; 3813 3814 /** 3815 * @deprecated replaced by {@link EDbObjectType#oracleHint}. 3816 * removed since v2.9.2.5 3817 */ 3818 // public final static int ttObjOracleHint = 70; 3819 3820 /** 3821 * @deprecated replaced by {@link EDbObjectType#fieldName}. 3822 * check {@link gudusoft.gsqlparser.nodes.TExpression#getFieldName()} for more 3823 * removed since v2.9.2.5 3824 */ 3825 // public final static int ttobjFieldName = 51; 3826 3827 /** 3828 * @deprecated replaced by {@link EDbObjectType#miningModel}. 3829 * removed since v2.9.2.5 3830 */ 3831 // public final static int ttobjMiningModel = 46; 3832 3833 /** 3834 * @deprecated replaced by {@link EDbObjectType#materializedView}. 3835 * removed since v2.9.2.5 3836 */ 3837 // public final static int ttobjMaterializedView = 44; 3838 3839 /** 3840 * @deprecated replaced by {@link EDbObjectType#indextype}. 3841 * removed since v2.9.2.5 3842 */ 3843 // public final static int ttobjIndexType = 42; 3844 3845 /** 3846 * @deprecated replaced by {@link EDbObjectType#operator}. 3847 * removed since v2.9.2.5 3848 */ 3849 // public final static int ttobjOperator = 40; 3850 3851 /** 3852 * @deprecated replaced by {@link EDbObjectType#server}. 3853 * server name in {@link #serverToken} 3854 * 3855 * removed since v2.9.2.5 3856 */ 3857 // public final static int ttobjServerName = 32; 3858 3859 /** 3860 * @deprecated replaced by {@link EDbObjectType#sequence}. 3861 * Sequence name in {@link #objectToken} 3862 * 3863 * removed since v2.9.2.5 3864 */ 3865 // public final static int ttobjSequence = 29; 3866 3867 /** 3868 * @deprecated replaced by {@link EDbObjectType#plsql_package}. 3869 * package name in {@link #objectToken} 3870 * 3871 * removed since v2.9.2.5 3872 */ 3873 // public final static int ttobjPackage = 28; 3874 3875 /** 3876 * @deprecated replaced by {@link EDbObjectType#alias}. 3877 * alias name in {@link #objectToken} 3878 * 3879 * removed since v2.9.2.5 3880 */ 3881 // public final static int ttobjAliasName = 25; 3882 3883 3884 /** 3885 * @deprecated replaced by {@link EDbObjectType#trigger}. 3886 * Trigger name in {@link #objectToken} 3887 * 3888 * removed since v2.9.2.5 3889 */ 3890 // public final static int ttobjTrigger = 24; 3891 3892 /** 3893 * @deprecated replaced by {@link EDbObjectType#database}. 3894 * Database name in {@link #objectToken} 3895 * 3896 * removed since v2.9.2.5 3897 */ 3898 // public final static int ttobjDatabaseName = 22; 3899 3900 /** 3901 * @deprecated replaced by {@link EDbObjectType#transaction}. 3902 * Transaction name in {@link #objectToken} 3903 * 3904 * removed since v2.9.2.5 3905 */ 3906 // public final static int ttobjTransactionName = 21; 3907 3908 3909 /** 3910 * @deprecated replaced by {@link EDbObjectType#user_defined_type}. 3911 * type name in {@link #objectToken} 3912 * 3913 * removed since v2.9.2.5 3914 */ 3915 // public final static int ttobjTypeName = 27; 3916 3917 /** 3918 * @deprecated replaced by {@link EDbObjectType#property}. 3919 * property name in {@link #propertyToken} 3920 * 3921 * removed since v2.9.2.5 3922 */ 3923 // public final static int ttobjPropertyName = 20; 3924 3925 /** 3926 * @deprecated replaced by {@link EDbObjectType#view}. 3927 * view name in {@link #objectToken} 3928 * 3929 * removed since v2.9.2.5 3930 */ 3931 // public final static int ttobjViewName = 18; 3932 3933 /** 3934 * @deprecated replaced by {@link EDbObjectType#cursor}. 3935 * cursor name in {@link #objectToken} 3936 * 3937 * removed since v2.9.2.5 3938 */ 3939 // public final static int ttobjCursorName = 17; 3940 3941 /** 3942 * @deprecated replaced by {@link EDbObjectType#materializedView}. 3943 * materialized view name in {@link #objectToken} 3944 * 3945 * removed since v2.9.2.5 3946 */ 3947 // public final static int ttobjMaterializedViewName = 16; 3948 3949 /** 3950 * @deprecated replaced by {@link EDbObjectType#index}. 3951 * index name in {@link #objectToken} 3952 * 3953 * removed since v2.9.2.5 3954 */ 3955 // public final static int ttobjIndexName = 15; 3956 3957 /** 3958 * @deprecated replaced by {@link EDbObjectType#label}. 3959 * label name in {@link #objectToken} 3960 * 3961 * removed since v2.9.2.5 3962 */ 3963 // public final static int ttobjLabelName = 14; 3964 3965 /** 3966 * @deprecated replaced by {@link EDbObjectType#procedure}. 3967 * procedure name in {@link #objectToken} 3968 * 3969 * removed since v2.9.2.5 3970 */ 3971 // public final static int ttobjProcedureName = 12; 3972 3973 /** 3974 * @deprecated replaced by {@link EDbObjectType#variable}. 3975 * table variable in objectToken. 3976 * 3977 * removed since v2.9.2.5 3978 */ 3979 // public final static int ttobjTableVar = 8; 3980 3981 /** 3982 * @deprecated replaced by {@link EDbObjectType#cte}. 3983 * table name in objectToken. 3984 * 3985 * removed since v2.9.2.5 3986 */ 3987 // public final static int ttobjTableCTE = 5; 3988 3989 /** 3990 * @deprecated replaced by {@link EDbObjectType}. 3991 * table name in objectToken. 3992 */ 3993 // public final static int ttobjTableTemp = 6; 3994 3995 /** 3996 * @deprecated replaced by {@link EDbObjectType}. 3997 */ 3998 // public final static int ttobjTablePivot = 7; 3999 4000 /** 4001 * @deprecated replaced by {@link EDbObjectType#table_alias}. 4002 * table alias in objectToken 4003 * 4004 * removed since v2.9.2.5 4005 */ 4006 // public final static int ttObjTableAlias = 4; 4007}