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 * <p><b>Note on the returned value</b>: for a qualified column 2275 * reference like {@code c.sbCustId} in {@code FROM sbCustomer c}, 2276 * this returns the <em>user-written qualifier</em> {@code "c"} (the 2277 * alias), not the resolved physical table {@code "sbCustomer"}. The 2278 * method does not disambiguate alias from table name — it returns 2279 * whatever literal appears before the dot in the source SQL. For 2280 * the resolved physical table, use {@link #getSourceTable()} and 2281 * read {@code TTable.getName()} on the result; that goes through 2282 * the two-phase semantic resolver which performs the alias → table 2283 * mapping. See <a href="https://www.sqlparser.com/bugs/mantisbt/view.php?id=4464">MantisBT 2284 * #4464</a> — a dedicated convenience accessor 2285 * {@code getResolvedTableName()} is proposed in that ticket to make 2286 * the resolved-table use case more discoverable; it will be added 2287 * additively without changing this method's behavior or signature. 2288 * 2289 * @return string of the table name, return null if empty. 2290 * @see #getSourceTable() 2291 */ 2292 public String getTableString(){ 2293 if (objectToken == null) return ""; 2294// else if (!((dbObjectType == EDbObjectType.table)||(dbObjectType == EDbObjectType.view))){ 2295// return ""; 2296// } 2297 else return objectToken.toString(); 2298 } 2299 2300 /** 2301 * String text of the object name 2302 * 2303 * @return string of the object name, return null if empty. 2304 */ 2305 public String getObjectString() { 2306 if (objectToken != null) 2307 return objectToken.getAstext(); 2308 else 2309 return "" ; 2310 } 2311 2312 /** 2313 * String text of the part name 2314 * 2315 * @return string of the part name, return null if empty. 2316 */ 2317 public String getPartString() { 2318 if (partToken != null) 2319 return partToken.getAstext(); 2320 else 2321 return "" ; 2322 } 2323 2324 // ===== Phase 5: Cached normalized getters (reduce repeated normalize() calls) ===== 2325 2326 private transient gudusoft.gsqlparser.sqlenv.IdentifierService identifierService; 2327 2328 /** 2329 * Lazy initialization of IdentifierService for normalized identifier caching 2330 */ 2331 private gudusoft.gsqlparser.sqlenv.IdentifierService getIdentifierService() { 2332 if (identifierService == null && sqlEnv != null) { 2333 gudusoft.gsqlparser.sqlenv.IdentifierProfile profile = gudusoft.gsqlparser.sqlenv.IdentifierProfile.forVendor( 2334 sqlEnv.getDBVendor(), 2335 gudusoft.gsqlparser.sqlenv.IdentifierProfile.VendorFlags.defaults() 2336 ); 2337 identifierService = new gudusoft.gsqlparser.sqlenv.IdentifierService(profile, null); 2338 } 2339 return identifierService; 2340 } 2341 2342 /** 2343 * Get normalized database string with caching (Phase 5 optimization). 2344 * 2345 * <p>This method caches the normalized database name to avoid repeated normalize() calls. 2346 * The cache is invalidated automatically when the IdentifierProfile changes (e.g., vendor switch). 2347 * 2348 * @return normalized database name, or empty string if not available 2349 */ 2350 public String getNormalizedDatabaseString() { 2351 if (sqlEnv == null) return getDatabaseString(); 2352 2353 try { 2354 gudusoft.gsqlparser.sqlenv.IdentifierService service = getIdentifierService(); 2355 if (service == null) return getDatabaseString(); 2356 2357 long currentFingerprint = service.getProfile().getFingerprint(); 2358 2359 // Check if cache is valid (not null and fingerprint matches) 2360 if (normalizedDatabase == null || cacheFingerprint != currentFingerprint) { 2361 String raw = getDatabaseString(); 2362 if (raw != null && !raw.isEmpty()) { 2363 normalizedDatabase = service.normalize(raw, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType.dotCatalog); 2364 } else { 2365 normalizedDatabase = ""; 2366 } 2367 cacheFingerprint = currentFingerprint; 2368 } 2369 2370 return normalizedDatabase; 2371 } catch (Throwable t) { 2372 // Fallback to non-cached on any error 2373 return getDatabaseString(); 2374 } 2375 } 2376 2377 /** 2378 * Get normalized schema string with caching (Phase 5 optimization). 2379 * 2380 * <p>This method caches the normalized schema name to avoid repeated normalize() calls. 2381 * The cache is invalidated automatically when the IdentifierProfile changes. 2382 * 2383 * @return normalized schema name, or empty string if not available 2384 */ 2385 public String getNormalizedSchemaString() { 2386 if (sqlEnv == null) return getSchemaString(); 2387 2388 try { 2389 gudusoft.gsqlparser.sqlenv.IdentifierService service = getIdentifierService(); 2390 if (service == null) return getSchemaString(); 2391 2392 long currentFingerprint = service.getProfile().getFingerprint(); 2393 2394 // Check if cache is valid 2395 if (normalizedSchema == null || cacheFingerprint != currentFingerprint) { 2396 String raw = getSchemaString(); 2397 if (raw != null && !raw.isEmpty()) { 2398 normalizedSchema = service.normalize(raw, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType.dotSchema); 2399 } else { 2400 normalizedSchema = ""; 2401 } 2402 cacheFingerprint = currentFingerprint; 2403 } 2404 2405 return normalizedSchema; 2406 } catch (Throwable t) { 2407 // Fallback to non-cached 2408 return getSchemaString(); 2409 } 2410 } 2411 2412 /** 2413 * Get normalized table string with caching (Phase 5 optimization). 2414 * 2415 * <p>This method caches the normalized table name to avoid repeated normalize() calls. 2416 * The cache is invalidated automatically when the IdentifierProfile changes. 2417 * 2418 * @return normalized table name, or empty string if not available 2419 */ 2420 public String getNormalizedTableString() { 2421 if (sqlEnv == null) return getTableString(); 2422 2423 try { 2424 gudusoft.gsqlparser.sqlenv.IdentifierService service = getIdentifierService(); 2425 if (service == null) return getTableString(); 2426 2427 long currentFingerprint = service.getProfile().getFingerprint(); 2428 2429 // Check if cache is valid 2430 if (normalizedTable == null || cacheFingerprint != currentFingerprint) { 2431 String raw = getTableString(); 2432 if (raw != null && !raw.isEmpty()) { 2433 normalizedTable = service.normalize(raw, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType.dotTable); 2434 } else { 2435 normalizedTable = ""; 2436 } 2437 cacheFingerprint = currentFingerprint; 2438 } 2439 2440 return normalizedTable; 2441 } catch (Throwable t) { 2442 // Fallback to non-cached 2443 return getTableString(); 2444 } 2445 } 2446 2447 /** 2448 * Get normalized column string with caching (Phase 5 optimization). 2449 * 2450 * <p>This method caches the normalized column name (from partToken) to avoid repeated normalize() calls. 2451 * The cache is invalidated automatically when the IdentifierProfile changes. 2452 * 2453 * @return normalized column name, or empty string if not available 2454 */ 2455 public String getNormalizedColumnString() { 2456 if (sqlEnv == null) return getPartString(); 2457 2458 try { 2459 gudusoft.gsqlparser.sqlenv.IdentifierService service = getIdentifierService(); 2460 if (service == null) return getPartString(); 2461 2462 long currentFingerprint = service.getProfile().getFingerprint(); 2463 2464 // Check if cache is valid 2465 if (normalizedColumn == null || cacheFingerprint != currentFingerprint) { 2466 String raw = getPartString(); 2467 if (raw != null && !raw.isEmpty()) { 2468 normalizedColumn = service.normalize(raw, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType.dotColumn); 2469 } else { 2470 normalizedColumn = ""; 2471 } 2472 cacheFingerprint = currentFingerprint; 2473 } 2474 2475 return normalizedColumn; 2476 } catch (Throwable t) { 2477 // Fallback to non-cached 2478 return getPartString(); 2479 } 2480 } 2481 2482 /** 2483 * Get normalized server string with caching (Phase 5 optimization). 2484 * 2485 * <p>This method caches the normalized server name to avoid repeated normalize() calls. 2486 * The cache is invalidated automatically when the IdentifierProfile changes. 2487 * 2488 * @return normalized server name, or empty string if not available 2489 */ 2490 public String getNormalizedServerString() { 2491 if (sqlEnv == null) return getServerString(); 2492 2493 try { 2494 gudusoft.gsqlparser.sqlenv.IdentifierService service = getIdentifierService(); 2495 if (service == null) return getServerString(); 2496 2497 long currentFingerprint = service.getProfile().getFingerprint(); 2498 2499 // Check if cache is valid 2500 if (normalizedServer == null || cacheFingerprint != currentFingerprint) { 2501 String raw = getServerString(); 2502 if (raw != null && !raw.isEmpty()) { 2503 // Use dotUnknown for server since there's no dotServer type 2504 normalizedServer = service.normalize(raw, gudusoft.gsqlparser.sqlenv.ESQLDataObjectType.dotUnknown); 2505 } else { 2506 normalizedServer = ""; 2507 } 2508 cacheFingerprint = currentFingerprint; 2509 } 2510 2511 return normalizedServer; 2512 } catch (Throwable t) { 2513 // Fallback to non-cached 2514 return getServerString(); 2515 } 2516 } 2517 2518 2519 public void setExclamationmark(TSourceToken exclamationmark) { 2520 this.exclamationmark = exclamationmark; 2521 } 2522 2523 private TSourceToken exclamationmark; // objectToken@!, ! is dblink 2524 2525 private Boolean isParsed = false; 2526 2527 private void parseObjectName(){ 2528 parseTablename(); 2529 } 2530 2531 private void parseTablename(){ 2532 if ((this.dbObjectType == EDbObjectType.variable) ||(this.dbObjectType == EDbObjectType.stage))return; 2533 2534 switch (this.dbvendor){ 2535 case dbvteradata: 2536 case dbvhive: 2537 if (objectToken != null){ 2538 databaseToken = objectToken; 2539 //databaseToken.setDbObjType(TObjectName.ttobjDatabaseName); 2540 databaseToken.setDbObjectType(EDbObjectType.database); 2541 schemaToken = objectToken; 2542 } 2543 objectToken = partToken; 2544 partToken = null; 2545 2546 break; 2547 default: 2548 if (databaseToken != null){ 2549 serverToken = databaseToken; 2550 //serverToken.setDbObjType(TObjectName.ttobjServerName); 2551 serverToken.setDbObjectType(EDbObjectType.server); 2552 } 2553 2554 if (schemaToken != null){ 2555 databaseToken = schemaToken; 2556 //databaseToken.setDbObjType(TObjectName.ttobjDatabaseName); 2557 databaseToken.setDbObjectType(EDbObjectType.database); 2558 } 2559 2560 if (objectToken != null){ 2561 schemaToken = objectToken; 2562 schemaToken.setDbObjType(TObjectName.ttobjSchemaName); 2563 } 2564 2565 objectToken = partToken; 2566 partToken = null; 2567 break; 2568 } 2569 2570 if (objectToken != null){ 2571 objectToken.setDbObjectType(this.dbObjectType); 2572 } 2573 } 2574 2575 private void parseVariableName(){ 2576 if (databaseToken != null){ 2577 serverToken = databaseToken; 2578 //serverToken.setDbObjType(TObjectName.ttobjServerName); 2579 serverToken.setDbObjectType(EDbObjectType.server); 2580 } 2581 2582 if (schemaToken != null){ 2583 databaseToken = schemaToken; 2584 // databaseToken.setDbObjType(TObjectName.ttobjDatabaseName); 2585 databaseToken.setDbObjectType(EDbObjectType.database); 2586 } 2587 2588 if (objectToken != null){ 2589 if (partToken != null){ 2590 schemaToken = objectToken; 2591 schemaToken.setDbObjType(TObjectName.ttobjSchemaName); 2592 objectToken = partToken; 2593 partToken = null; 2594 }else{ 2595 2596 } 2597 }else{ 2598 objectToken = partToken; 2599 partToken = null; 2600 } 2601 } 2602 2603 private String ansiSchemaName; 2604 private String ansiCatalogName; 2605 2606 2607 /** 2608 * In this SQL: select * from part1.part2, 2609 * In Hive, MySQL and Teradata, part1 will be treated as a database name, returned in getDatabaseString(), 2610 * while getSchemaString() return empty string. 2611 * 2612 * However, TObjectName.getAnsiSchemaName() will return part1, which means it's a schema name. 2613 * 2614 * If a table name is not qualified with a schema name, but GSP detect the schema for this table in the metadata 2615 * then, this method will return this detected schema name. 2616 * 2617 * @return schema name 2618 */ 2619 public String getAnsiSchemaName(){ 2620 String ret = this.getSchemaString(); 2621 if ((ret.length() == 0) && ((this.getImplictSchemaString() != null) && (!this.getImplictSchemaString().equalsIgnoreCase("default")))){ 2622 ret = this.getImplictSchemaString(); 2623 } 2624 2625 switch (dbvendor){ 2626 case dbvmysql: 2627 case dbvhive: 2628 case dbvteradata: 2629 case dbvimpala: 2630 ret = this.getDatabaseString(); 2631 break; 2632 } 2633 return ret; 2634 } 2635 2636 /** 2637 * If a table name is not qualified with a database name, but GSP detect the database for this table in the metadata 2638 * then, this method will return this detected database name. 2639 * 2640 * @return 2641 */ 2642 public String getAnsiCatalogName(){ 2643 String ret = this.getDatabaseString(); 2644 if (( ret.length() == 0) && (this.getImplictDatabaseString() != null) && (!this.getImplictDatabaseString().equalsIgnoreCase("default"))){ 2645 ret = this.getImplictDatabaseString(); 2646 } 2647 2648 switch (dbvendor){ 2649 case dbvmysql: 2650 case dbvhive: 2651 case dbvteradata: 2652 case dbvimpala: 2653 ret = ""; 2654 break; 2655 } 2656 2657 return ret; 2658 } 2659 2660 private void parseFunctionName(){ 2661 this.parseTablename(); 2662 } 2663 2664 private void parseColumnMethodName(){ 2665 // objectType = ttobjColumnMethod; 2666 2667 methodToken = objectToken; 2668 partToken = schemaToken; 2669 2670 objectToken = null;//; 2671 schemaToken = null; 2672 } 2673 2674 private void parseColumnName(){ 2675 assert(partToken != null); 2676 } 2677 2678 public TObjectName(){ 2679 } 2680 2681 /** 2682 * List the number of parts made up this objectName 2683 * 2684 * @return the number of parts that made up this objectName 2685 */ 2686 public int getNumberOfPart() { 2687 return numberOfPart; 2688 } 2689 2690 private int numberOfPart = 1; 2691 2692 public static TObjectName createObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType){ 2693 return new TObjectName(dbVendor,dbObjectType); 2694 } 2695 2696 2697 public static TObjectName createObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token1){ 2698 return new TObjectName(dbVendor,dbObjectType,token1); 2699 } 2700 2701 public static TObjectName createObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType, String str) { 2702 String[] parts = str.split("\\."); 2703 if (parts.length == 1) { 2704 return new TObjectName(dbVendor, dbObjectType, new TSourceToken(parts[0])); 2705 } else if (parts.length == 2) { 2706 return new TObjectName(dbVendor, dbObjectType, new TSourceToken(parts[0]), new TSourceToken(parts[1])); 2707 } else if (parts.length == 3) { 2708 return new TObjectName(dbVendor, dbObjectType, new TSourceToken(parts[0]), new TSourceToken(parts[1]), new TSourceToken(parts[2])); 2709 } else if (parts.length == 4) { 2710 return new TObjectName(dbVendor, dbObjectType, new TSourceToken(parts[0]), new TSourceToken(parts[1]), new TSourceToken(parts[2]), new TSourceToken(parts[3])); 2711 } 2712 return new TObjectName(dbVendor, dbObjectType, new TSourceToken(str)); 2713 } 2714 2715 2716 public static TObjectName createObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token1,TSourceToken token2){ 2717 return new TObjectName(dbVendor,dbObjectType,token1,token2); 2718 } 2719 2720 public static TObjectName createObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token1,TSourceToken token2,TSourceToken token3){ 2721 return new TObjectName(dbVendor,dbObjectType,token1,token2,token3); 2722 } 2723 2724 public static TObjectName createObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token1,TSourceToken token2,TSourceToken token3,TSourceToken token4){ 2725 return new TObjectName(dbVendor,dbObjectType,token1,token2,token3,token4); 2726 } 2727 2728 /** 2729 * @deprecated As of v2.0.7.1, please use {@link #TObjectName(EDbObjectType, TSourceToken)} instead. 2730 * 2731 * Class constructor specifying object name and object type. 2732 * <p> 2733 * Use {@link gudusoft.gsqlparser.TGSqlParser#parseObjectName} to create an objectName more than 2 parts. 2734 * 2735 * @param token name of this object 2736 * @param dbObjectType type of this object 2737 */ 2738 private TObjectName(TSourceToken token,EDbObjectType dbObjectType){ 2739 this(dbObjectType,token); 2740 } 2741 2742 public void splitNameInQuotedIdentifier(){ 2743 if (this.dbvendor != EDbVendor.dbvbigquery && this.dbvendor != EDbVendor.dbvsnowflake) return; 2744 if (this.objectToken == null) return; 2745 TSourceToken token = this.objectToken; 2746 2747 // For Snowflake IDENTIFIER function, the token is a single-quoted string literal 2748 // which has quoteType notQuoted but starts with single quote 2749 boolean isSnowflakeSingleQuotedString = (this.dbvendor == EDbVendor.dbvsnowflake) 2750 && token.toString().startsWith("'"); 2751 2752 if (getQuoteType() == EQuoteType.notQuoted && !isSnowflakeSingleQuotedString) return; 2753// if ((this.dbvendor != EDbVendor.dbvbigquery) 2754// &&(getQuoteType() == EQuoteType.doubleQuote)) return; 2755 2756 String tokenStr = token.toString(); 2757 char outerQuoteChar = tokenStr.charAt(0); 2758 String s = TBaseType.getTextWithoutQuoted(tokenStr); 2759 String[] a = s.split("[.]"); 2760 if (a.length == 1){ 2761 // this.objectToken = token; 2762 }else if (a.length == 2){ 2763 String objPart = a[1]; 2764 String schemaPart = a[0]; 2765 2766 // For Snowflake IDENTIFIER function with single-quoted string containing double-quoted parts 2767 // e.g., IDENTIFIER('"SCHEMA"."TABLE"') -> parts already have double quotes 2768 // For unquoted parts, e.g., IDENTIFIER('schema.table') -> use parts as-is 2769 boolean isSnowflakeIdentifierFunction = (this.dbvendor == EDbVendor.dbvsnowflake) && (outerQuoteChar == '\''); 2770 2771 if (isSnowflakeIdentifierFunction) { 2772 this.objectToken = new TSourceToken(objPart); 2773 this.schemaToken = new TSourceToken(schemaPart); 2774 } else { 2775 this.objectToken = new TSourceToken(outerQuoteChar + objPart + outerQuoteChar); 2776 this.schemaToken = new TSourceToken(outerQuoteChar + schemaPart + outerQuoteChar); 2777 } 2778 }else if (a.length == 3){ 2779 String objPart = a[2]; 2780 String schemaPart = a[1]; 2781 String dbPart = a[0]; 2782 2783 boolean isSnowflakeIdentifierFunction = (this.dbvendor == EDbVendor.dbvsnowflake) && (outerQuoteChar == '\''); 2784 2785 if (isSnowflakeIdentifierFunction) { 2786 this.objectToken = new TSourceToken(objPart); 2787 this.schemaToken = new TSourceToken(schemaPart); 2788 this.databaseToken = new TSourceToken(dbPart); 2789 } else { 2790 this.objectToken = new TSourceToken(outerQuoteChar + objPart + outerQuoteChar); 2791 this.schemaToken = new TSourceToken(outerQuoteChar + schemaPart + outerQuoteChar); 2792 this.databaseToken = new TSourceToken(outerQuoteChar + dbPart + outerQuoteChar); 2793 } 2794 } 2795 2796 } 2797 2798 private TObjectName(EDbVendor dbVendor){ 2799 this.dbvendor = dbVendor; 2800 } 2801 2802 private TObjectName(EDbVendor dbVendor,EDbObjectType dbObjectType){ 2803 this.dbvendor = dbVendor; 2804 this.dbObjectType = dbObjectType; 2805 } 2806 2807 private TObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token){ 2808 this.dbvendor = dbVendor; 2809 numberOfPart = 1; 2810 this.setStartToken(token); 2811 this.setEndToken(token); 2812 2813 this.dbObjectType = dbObjectType; 2814 switch (dbObjectType){ 2815 case column: 2816 this.partToken = token; 2817 break; 2818 case method: 2819 this.methodToken = token; 2820 break; 2821 case table: 2822 case function: 2823 case procedure: 2824 case materializedView: 2825 case alias: 2826 case module: 2827 case sequence: 2828 case collation: 2829 this.objectToken = token; 2830 splitNameInQuotedIdentifier(); 2831 break; 2832 default: 2833 this.objectToken = token; 2834 break; 2835 } 2836 } 2837 2838 private void initWithOneToken(EDbObjectType dbObjectType,TSourceToken token){ 2839 numberOfPart = 1; 2840 this.setStartToken(token); 2841 this.setEndToken(token); 2842 2843 this.dbObjectType = dbObjectType; 2844 switch (dbObjectType){ 2845 case column: 2846 this.partToken = token; 2847 break; 2848 case method: 2849 this.methodToken = token; 2850 break; 2851 case table: 2852 case function: 2853 case procedure: 2854 case materializedView: 2855 case alias: 2856 case module: 2857 case sequence: 2858 case collation: 2859 case stage: 2860 this.objectToken = token; 2861 splitNameInQuotedIdentifier(); 2862 break; 2863 case namespace: 2864 this.schemaToken = token; 2865 break; 2866 default: 2867 this.objectToken = token; 2868 break; 2869 } 2870 } 2871 2872 private void initWithTwoTokens(EDbObjectType dbObjectType,TSourceToken token2,TSourceToken token1){ 2873 initWithOneToken(dbObjectType,token1); 2874 numberOfPart = 2; 2875 this.setStartToken(token2); 2876 this.setEndToken(token1); 2877 2878 switch (dbObjectType){ 2879 case column: 2880 this.objectToken = token2; 2881 break; 2882 case method: 2883 this.partToken = token2; 2884 break; 2885 case table: 2886 case function: 2887 case procedure: 2888 case materializedView: 2889 case alias: 2890 case module: 2891 case sequence: 2892 case collation: 2893 case stage: 2894 this.schemaToken = token2; 2895 break; 2896 case namespace: 2897 this.databaseToken = token2; 2898 break; 2899 default: 2900 this.schemaToken = token2; 2901 break; 2902 } 2903 2904 } 2905 2906 private void initWithThreeTokens(EDbObjectType dbObjectType,TSourceToken token3,TSourceToken token2,TSourceToken token1){ 2907 initWithTwoTokens(dbObjectType,token2,token1); 2908 numberOfPart = 3; 2909 this.setStartToken(token3); 2910 this.setEndToken(token1); 2911 2912 switch (dbObjectType){ 2913 case column: 2914 this.schemaToken = token3; 2915 break; 2916 case method: 2917 this.objectToken = token3; 2918 break; 2919 case table: 2920 case function: 2921 case procedure: 2922 case materializedView: 2923 case alias: 2924 case module: 2925 case sequence: 2926 case collation: 2927 case stage: 2928 this.databaseToken = token3; 2929 break; 2930 default: 2931 this.databaseToken = token3; 2932 break; 2933 } 2934 2935 } 2936 2937 private void initWithFourTokens(EDbObjectType dbObjectType,TSourceToken token4,TSourceToken token3,TSourceToken token2,TSourceToken token1){ 2938 initWithThreeTokens(dbObjectType,token3,token2,token1); 2939 numberOfPart = 4; 2940 this.setStartToken(token4); 2941 this.setEndToken(token1); 2942 2943 switch (dbObjectType){ 2944 case column: 2945 this.databaseToken = token4; 2946 break; 2947 case method: 2948 this.schemaToken = token4; 2949 break; 2950 case table: 2951 case function: 2952 case procedure: 2953 case materializedView: 2954 case alias: 2955 case module: 2956 case sequence: 2957 case collation: 2958 this.serverToken = token4; 2959 break; 2960 default: 2961 this.serverToken = token4; 2962 break; 2963 } 2964 2965 } 2966 2967 /** 2968 * @deprecated As of v2.0.7.1, please use {@link TObjectName#createObjectName(EDbVendor, EDbObjectType, TSourceToken)} instead. 2969 * 2970 * @param dbObjectType 2971 * @param token 2972 */ 2973 private TObjectName(EDbObjectType dbObjectType,TSourceToken token){ 2974 initWithOneToken(dbObjectType,token); 2975 } 2976 2977 private TObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token2,TSourceToken token1){ 2978 this(dbVendor,dbObjectType,token1); 2979 numberOfPart = 2; 2980 this.setStartToken(token2); 2981 this.setEndToken(token1); 2982 2983 switch (dbObjectType){ 2984 case column: 2985 this.objectToken = token2; 2986 break; 2987 case method: 2988 this.partToken = token2; 2989 break; 2990 case table: 2991 case function: 2992 case procedure: 2993 case materializedView: 2994 case alias: 2995 case module: 2996 case sequence: 2997 case collation: 2998 if (dbVendor == EDbVendor.dbvteradata){ 2999 this.databaseToken = token2; 3000 }else{ 3001 this.schemaToken = token2; 3002 } 3003 3004 break; 3005 default: 3006 this.schemaToken = token2; 3007 break; 3008 } 3009 3010 } 3011 3012 3013 /** 3014 * @deprecated since ver 2.5.9.8 3015 * 3016 * @param dbObjectType 3017 * @param token2 3018 * @param token1 3019 */ 3020 private TObjectName(EDbObjectType dbObjectType,TSourceToken token2,TSourceToken token1){ 3021 this(dbObjectType,token1); 3022 numberOfPart = 2; 3023 this.setStartToken(token2); 3024 this.setEndToken(token1); 3025 3026 switch (dbObjectType){ 3027 case column: 3028 this.objectToken = token2; 3029 break; 3030 case method: 3031 this.partToken = token2; 3032 break; 3033 case table: 3034 case function: 3035 case procedure: 3036 case materializedView: 3037 case alias: 3038 case module: 3039 case sequence: 3040 case collation: 3041 this.schemaToken = token2; 3042 break; 3043 default: 3044 this.schemaToken = token2; 3045 break; 3046 } 3047 } 3048 private TObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token3,TSourceToken token2,TSourceToken token1){ 3049 this(dbVendor,dbObjectType,token2,token1); 3050 numberOfPart = 3; 3051 this.setStartToken(token3); 3052 this.setEndToken(token1); 3053 3054 switch (dbObjectType){ 3055 case column: 3056 this.schemaToken = token3; 3057 break; 3058 case method: 3059 this.objectToken = token3; 3060 break; 3061 case table: 3062 case function: 3063 case procedure: 3064 case materializedView: 3065 case alias: 3066 case module: 3067 case sequence: 3068 case collation: 3069 this.databaseToken = token3; 3070 break; 3071 default: 3072 this.databaseToken = token3; 3073 break; 3074 } 3075 3076 } 3077 3078 3079 /** 3080 * @deprecated since ver 2.5.9.8 3081 * 3082 * @param dbObjectType 3083 * @param token3 3084 * @param token2 3085 * @param token1 3086 */ 3087 private TObjectName(EDbObjectType dbObjectType,TSourceToken token3,TSourceToken token2,TSourceToken token1){ 3088 this(dbObjectType,token2,token1); 3089 numberOfPart = 3; 3090 this.setStartToken(token3); 3091 this.setEndToken(token1); 3092 3093 switch (dbObjectType){ 3094 case column: 3095 this.schemaToken = token3; 3096 break; 3097 case method: 3098 this.objectToken = token3; 3099 break; 3100 case table: 3101 case function: 3102 case procedure: 3103 case materializedView: 3104 case alias: 3105 case module: 3106 case sequence: 3107 case collation: 3108 this.databaseToken = token3; 3109 break; 3110 default: 3111 this.databaseToken = token3; 3112 break; 3113 } 3114 } 3115 3116 private TObjectName(EDbVendor dbVendor, EDbObjectType dbObjectType,TSourceToken token4,TSourceToken token3,TSourceToken token2,TSourceToken token1){ 3117 this(dbVendor,dbObjectType,token3,token2,token1); 3118 numberOfPart = 4; 3119 this.setStartToken(token4); 3120 this.setEndToken(token1); 3121 3122 switch (dbObjectType){ 3123 case column: 3124 this.databaseToken = token4; 3125 break; 3126 case method: 3127 this.schemaToken = token4; 3128 break; 3129 case table: 3130 case function: 3131 case procedure: 3132 case materializedView: 3133 case alias: 3134 case module: 3135 case sequence: 3136 case collation: 3137 this.serverToken = token4; 3138 break; 3139 default: 3140 this.serverToken = token4; 3141 break; 3142 } 3143 3144 } 3145 3146 /** 3147 * @deprecated since ver 2.5.9.8 3148 * 3149 * @param dbObjectType 3150 * @param token4 3151 * @param token3 3152 * @param token2 3153 * @param token1 3154 */ 3155 private TObjectName(EDbObjectType dbObjectType,TSourceToken token4,TSourceToken token3,TSourceToken token2,TSourceToken token1){ 3156 this(dbObjectType,token3,token2,token1); 3157 numberOfPart = 4; 3158 this.setStartToken(token4); 3159 this.setEndToken(token1); 3160 3161 switch (dbObjectType){ 3162 case column: 3163 this.databaseToken = token4; 3164 break; 3165 case method: 3166 this.schemaToken = token4; 3167 break; 3168 case table: 3169 case function: 3170 case procedure: 3171 case materializedView: 3172 case alias: 3173 case module: 3174 case sequence: 3175 case collation: 3176 this.serverToken = token4; 3177 break; 3178 default: 3179 this.serverToken = token4; 3180 break; 3181 } 3182 } 3183 3184 /** 3185 * @deprecated As of v2.0.7.1, please use {@link #TObjectName(EDbObjectType, TSourceToken, TSourceToken)} instead. 3186 * 3187 * Class constructor specifying object, part name and object type. 3188 * Use this constructor to create a <code>table.column</code> objectName. 3189 * Use {@link gudusoft.gsqlparser.TGSqlParser#parseObjectName} to create an objectName more than 2 parts. 3190 * 3191 * @param pObjectToken name of this object, usually it's the table name 3192 * @param pPartToken name of the column 3193 * @param dbObjectType type of this object, usually it's {@link gudusoft.gsqlparser.EDbObjectType#column} 3194 */ 3195 private TObjectName(TSourceToken pObjectToken,TSourceToken pPartToken,EDbObjectType dbObjectType){ 3196 this(dbObjectType,pObjectToken,pPartToken); 3197 } 3198 3199 public void init(Object arg1) 3200 { 3201 partToken = (TSourceToken)arg1; 3202 numberOfPart = 1; 3203 this.setStartToken(partToken); 3204 this.setEndToken(partToken); 3205 } 3206 3207 public void init(Object arg1, Object arg2) 3208 { 3209 if (arg1 instanceof EDbObjectType){ 3210 initWithOneToken((EDbObjectType)arg1,(TSourceToken) arg2); 3211 3212 }else{ 3213 numberOfPart = 0; 3214 objectToken = (TSourceToken)arg1; 3215 partToken = (TSourceToken)arg2; 3216 if (partToken != null) numberOfPart++; 3217 if (objectToken != null) numberOfPart++; 3218 3219 3220 if(objectToken != null){ 3221 this.setStartToken(objectToken); 3222 }else{ 3223 this.setStartToken(partToken); 3224 } 3225 3226 if (partToken != null){ 3227 this.setEndToken(partToken); 3228 }else{ 3229 this.setEndToken(objectToken); 3230 } 3231 } 3232 } 3233 3234 public void init(EDbObjectType dbObjectType, Object arg1, Object arg2, Object arg3){ 3235 numberOfPart = 0; 3236 if (arg1 != null) numberOfPart++; 3237 if (arg2 != null) numberOfPart++; 3238 if (arg3 != null) numberOfPart++; 3239 3240 this.dbObjectType = dbObjectType; 3241 this.setStartToken((TSourceToken)arg1); 3242 this.setEndToken((TSourceToken)arg3); 3243 switch (this.dbObjectType){ 3244 case column: 3245 schemaToken = (TSourceToken)arg1; 3246 objectToken = (TSourceToken)arg2; 3247 partToken = (TSourceToken)arg3; 3248 break; 3249 case table: 3250 case function: 3251 case procedure: 3252 case materializedView: 3253 case module: 3254 case sequence: 3255 databaseToken = (TSourceToken) arg1; 3256 schemaToken = (TSourceToken)arg2; 3257 objectToken = (TSourceToken)arg3; 3258 break; 3259 case alias: 3260 break; 3261 default: 3262 break; 3263 } 3264 3265 } 3266 3267 public void init(Object arg1, Object arg2, Object arg3) 3268 { 3269 if (arg1 instanceof EDbObjectType){ 3270 initWithTwoTokens((EDbObjectType)arg1,(TSourceToken) arg2,(TSourceToken) arg3); 3271 }else{ 3272 numberOfPart = 0; 3273 if (arg1 != null) numberOfPart++; 3274 if (arg2 != null) numberOfPart++; 3275 if (arg3 != null) numberOfPart++; 3276 3277 if (dbvendor == EDbVendor.dbvteradata){ 3278 databaseToken = (TSourceToken) arg1; 3279 this.setStartToken(databaseToken); 3280 }else{ 3281 schemaToken = (TSourceToken)arg1; 3282 this.setStartToken(schemaToken); 3283 if (schemaToken != null) 3284 {schemaToken.setDbObjType(TObjectName.ttobjSchemaName);} 3285 } 3286 3287 objectToken = (TSourceToken)arg2; 3288 partToken = (TSourceToken)arg3; 3289 this.setEndToken(partToken); 3290 } 3291 } 3292 3293 public void init(Object arg1, Object arg2, Object arg3, Object arg4) 3294 { 3295 if (arg1 instanceof EDbObjectType){ 3296 //this.dbObjectType = (EDbObjectType)arg1; 3297 //init(arg2,arg3,arg4); 3298 initWithThreeTokens((EDbObjectType)arg1,(TSourceToken)arg2,(TSourceToken)arg3,(TSourceToken)arg4); 3299 }else{ 3300 numberOfPart = 0; 3301 if (arg1 != null) numberOfPart++; 3302 if (arg2 != null) numberOfPart++; 3303 if (arg3 != null) numberOfPart++; 3304 if (arg4 != null) numberOfPart++; 3305 3306 //serverToken = (TSourceToken)arg1; 3307 databaseToken = (TSourceToken)arg1; 3308 schemaToken = (TSourceToken)arg2; 3309 objectToken = (TSourceToken)arg3; 3310 partToken = (TSourceToken)arg4; 3311 this.setStartToken(databaseToken); 3312 this.setEndToken(partToken); 3313 if (databaseToken != null){ 3314 //databaseToken.setDbObjType(TObjectName.ttobjDatabaseName); 3315 databaseToken.setDbObjectType(EDbObjectType.database); 3316 }else { 3317 } 3318 if (schemaToken != null){ 3319 schemaToken.setDbObjType(TObjectName.ttobjSchemaName); 3320 }else { 3321 } 3322 } 3323 } 3324 3325 public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) 3326 { 3327 numberOfPart = 0; 3328 if (arg1 != null) numberOfPart++; 3329 if (arg2 != null) numberOfPart++; 3330 if (arg3 != null) numberOfPart++; 3331 if (arg4 != null) numberOfPart++; 3332 if (arg5 != null) numberOfPart++; 3333 3334 serverToken = (TSourceToken)arg1; 3335 databaseToken = (TSourceToken)arg2; 3336 schemaToken = (TSourceToken)arg3; 3337 objectToken = (TSourceToken)arg4; 3338 partToken = (TSourceToken)arg5; 3339 3340 this.setStartToken(serverToken); 3341 this.setEndToken(partToken); 3342 3343 if (serverToken != null){ 3344 //serverToken.setDbObjType(TObjectName.ttobjServerName); 3345 serverToken.setDbObjectType(EDbObjectType.server); 3346 }else{ 3347 } 3348 if (databaseToken != null){ 3349 //databaseToken.setDbObjType(TObjectName.ttobjDatabaseName); 3350 databaseToken.setDbObjectType(EDbObjectType.database); 3351 }else{ 3352 } 3353 if (schemaToken != null){ 3354 schemaToken.setDbObjType(TObjectName.ttobjSchemaName); 3355 }else{ 3356 } 3357 } 3358 3359 public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) 3360 { 3361 numberOfPart = 0; 3362 if (arg1 != null) numberOfPart++; 3363 if (arg2 != null) numberOfPart++; 3364 if (arg3 != null) numberOfPart++; 3365 if (arg4 != null) numberOfPart++; 3366 if (arg5 != null) numberOfPart++; 3367 if (arg6 != null) numberOfPart++; 3368 3369 serverToken = (TSourceToken)arg1; 3370 databaseToken = (TSourceToken)arg2; 3371 schemaToken = (TSourceToken)arg3; 3372 objectToken = (TSourceToken)arg4; 3373 partToken = (TSourceToken)arg5; 3374 propertyToken = (TSourceToken)arg6; 3375 3376 this.setStartToken(serverToken); 3377 this.setEndToken(propertyToken); 3378 3379 if (serverToken != null){ 3380 //serverToken.setDbObjType(TObjectName.ttobjServerName); 3381 serverToken.setDbObjectType(EDbObjectType.server); 3382 }else{ 3383 } 3384 if (databaseToken != null){ 3385 // databaseToken.setDbObjType(TObjectName.ttobjDatabaseName); 3386 databaseToken.setDbObjectType(EDbObjectType.database); 3387 }else{ 3388 } 3389 if (schemaToken != null){ 3390 schemaToken.setDbObjType(TObjectName.ttobjSchemaName); 3391 }else{ 3392 } 3393 } 3394 3395 /** 3396 * Init with 7 tokens for deeply nested struct field access (e.g., BigQuery) 3397 * Pattern: a.b.c.d.e.f.g (7 parts) 3398 */ 3399 public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7) 3400 { 3401 // First 6 parts use standard tokens 3402 init(arg1, arg2, arg3, arg4, arg5, arg6); 3403 3404 // 7th part goes to additionalParts 3405 if (arg7 != null) { 3406 if (additionalParts == null) { 3407 additionalParts = new java.util.ArrayList<>(); 3408 } 3409 additionalParts.add((TSourceToken) arg7); 3410 numberOfPart++; 3411 this.setEndToken((TSourceToken) arg7); 3412 } 3413 } 3414 3415 /** 3416 * Init with 8 tokens for deeply nested struct field access 3417 * Pattern: a.b.c.d.e.f.g.h (8 parts) 3418 */ 3419 public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8) 3420 { 3421 init(arg1, arg2, arg3, arg4, arg5, arg6, arg7); 3422 3423 if (arg8 != null) { 3424 if (additionalParts == null) { 3425 additionalParts = new java.util.ArrayList<>(); 3426 } 3427 additionalParts.add((TSourceToken) arg8); 3428 numberOfPart++; 3429 this.setEndToken((TSourceToken) arg8); 3430 } 3431 } 3432 3433 /** 3434 * Init with 9 tokens for deeply nested struct field access 3435 * Pattern: a.b.c.d.e.f.g.h.i (9 parts) 3436 */ 3437 public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9) 3438 { 3439 init(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); 3440 3441 if (arg9 != null) { 3442 if (additionalParts == null) { 3443 additionalParts = new java.util.ArrayList<>(); 3444 } 3445 additionalParts.add((TSourceToken) arg9); 3446 numberOfPart++; 3447 this.setEndToken((TSourceToken) arg9); 3448 } 3449 } 3450 3451 /** 3452 * Init with 10 tokens for deeply nested struct field access 3453 * Pattern: a.b.c.d.e.f.g.h.i.j (10 parts) 3454 */ 3455 public void init(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, Object arg8, Object arg9, Object arg10) 3456 { 3457 init(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9); 3458 3459 if (arg10 != null) { 3460 if (additionalParts == null) { 3461 additionalParts = new java.util.ArrayList<>(); 3462 } 3463 additionalParts.add((TSourceToken) arg10); 3464 numberOfPart++; 3465 this.setEndToken((TSourceToken) arg10); 3466 } 3467 } 3468 3469 /** 3470 * Get additional parts beyond the standard 6 tokens. 3471 * Used for deeply nested struct field access in databases like BigQuery. 3472 * 3473 * @return list of additional source tokens, or null if none 3474 */ 3475 public java.util.List<TSourceToken> getAdditionalParts() { 3476 return additionalParts; 3477 } 3478 3479 /** 3480 * The column position of this objectName in the SQL 3481 * 3482 * @return column position 3483 */ 3484 @Override 3485 public long getColumnNo() { 3486 long retval = -1; 3487 if (partToken != null) { retval = partToken.columnNo;} 3488 if (objectToken != null) { 3489 retval = objectToken.columnNo; 3490 } 3491 if (schemaToken != null) { 3492 retval = schemaToken.columnNo; 3493 } 3494 if (databaseToken != null) { 3495 retval = databaseToken.columnNo; 3496 } 3497 if (serverToken != null) { 3498 retval = serverToken.columnNo; 3499 } 3500 return retval; 3501 } 3502 3503 /** 3504 * The line number of this objectName in SQL 3505 * 3506 * @return the line number 3507 */ 3508 @Override 3509 public long getLineNo() { 3510 long retval = -1; 3511 if (partToken != null) { retval = partToken.lineNo;} 3512 if (objectToken != null) { 3513 retval = objectToken.lineNo; 3514 } 3515 if (schemaToken != null) { 3516 retval = schemaToken.lineNo; 3517 } 3518 if (databaseToken != null) { 3519 retval = databaseToken.lineNo; 3520 } 3521 if (serverToken != null) { 3522 retval = serverToken.lineNo; 3523 } 3524 return retval; 3525 } 3526 3527 private TObjectNameList referencedObjects = null; 3528 3529 public TObjectNameList getReferencedObjects() { 3530 if (referencedObjects == null){ 3531 referencedObjects = new TObjectNameList(); 3532 } 3533 return referencedObjects; 3534 } 3535 3536 /** 3537 * Returns only the column name if it's prefixed with a table name 3538 * 3539 * @return only the column name if it's prefixed with a table name 3540 */ 3541 public String getColumnNameOnly(){ 3542 3543 if (getPartToken() == null) return ""; 3544 else return getPartToken().toString(); 3545 } 3546 3547 public void accept(TParseTreeVisitor v){ 3548 v.preVisit(this); 3549 v.postVisit(this); 3550 } 3551 3552 public void acceptChildren(TParseTreeVisitor v){ 3553 v.preVisit(this); 3554 v.postVisit(this); 3555 } 3556 3557// private TSourceToken sortType = null; 3558// 3559// public void setSortType(TSourceToken sortType) { 3560// this.sortType = sortType; 3561// } 3562// 3563// /** 3564// * When this object is column in primary key(column,...), unique key(column,...) in sql server 3565// * there maybe sort information like column asc, column desc 3566// * this token represents for ASC, DESC if specified. 3567// * 3568// * @return ASC, DESC or null 3569// */ 3570// 3571// public TSourceToken getSortType() { 3572// return sortType; 3573// } 3574 3575 /** 3576 * It's the same as {@link #getPartToken} if {@link #getDbObjectType} is {@link gudusoft.gsqlparser.EDbObjectType#column} 3577 * 3578 * @return source token that represents column, return null if this objectName is not type of column 3579 * 3580 */ 3581 public TSourceToken getColumnToken(){ 3582 TSourceToken ret = null; 3583 if (this.getObjectType() == ttobjColumn){ 3584 ret = this.getPartToken(); 3585 } 3586 return ret; 3587 } 3588 3589 /* 3590 * re-arranage objectname to make it a valid name includes attribute name 3591 * used by teradata yacc file only. 3592 * valid syntax: 3593 * column.attr1().attr2(). 3594 * column.attr1().attr2().attr3() 3595 * table.column.attr1().attr2(). 3596 * table.column.attr1().attr2().attr3() 3597 * 3598 * @return 3599 */ 3600 public boolean isAttributeNameInObjectName(TSourceToken leftparen,TSourceToken rightparen){ 3601 boolean ret = false; 3602 if ((this.partToken == null) || (this.objectToken == null)){ 3603 return ret; 3604 } 3605 this.objectType = TObjectName.ttobjColumn; 3606 this.columnAttributes = new TObjectNameList(); 3607 TObjectName attr1 = new TObjectName(); 3608 attr1.objectType = TObjectName.ttobjAttribute; 3609 attr1.init(this.partToken); 3610 attr1.setEndToken(rightparen); 3611 this.columnAttributes.addObjectName(attr1); 3612 3613 this.partToken = this.objectToken; 3614 3615 if (this.schemaToken != null){ 3616 this.objectToken = this.schemaToken; 3617 } 3618 3619 return true; 3620 } 3621 3622 /** 3623 * Used internally in hive .y file to merge two objectNames 3624 */ 3625 public void mergeObjectName(TObjectName objectName){ 3626 this.objectToken = this.partToken; 3627 this.partToken = objectName.getPartToken(); 3628 this.setStartToken(objectToken); 3629 this.setEndToken(partToken); 3630 } 3631 3632 public void mergeObjectName(TObjectName objectName,TObjectName objectName2){ 3633 this.schemaToken = this.partToken; 3634 this.objectToken = objectName.getPartToken(); 3635 this.partToken = objectName2.getPartToken(); 3636 this.setStartToken(schemaToken); 3637 this.setEndToken(partToken); 3638 } 3639 3640 3641 public void columnToProperty(){ 3642 // if (numberOfPart == 1) return; 3643 if (this.pseudoTableType != EPseudoTableType.none) return; // pseudo table tokens already in correct position 3644 if (this.propertyToken != null) return; // columnToProperty() already called 3645 if (! ((this.partToken != null) && (this.objectToken != null))) return; // 既然是 column.property , 那么 partToken and objectToken 不能为空 3646 3647 this.propertyToken = this.partToken; 3648 this.partToken = this.objectToken; 3649 this.objectToken = this.schemaToken; 3650 3651 this.setDbObjectTypeDirectly(EDbObjectType.column); 3652 } 3653 3654 public void appendObjectName(TObjectName objectName){ 3655 if (this.databaseToken != null){ 3656 this.serverToken = this.databaseToken; 3657 } 3658 if (this.schemaToken != null){ 3659 this.databaseToken = this.schemaToken; 3660 } 3661 if (this.objectToken != null){ 3662 this.schemaToken = this.objectToken; 3663 } 3664 this.objectToken = this.partToken; 3665 this.partToken = objectName.getPartToken(); 3666 this.setEndTokenDirectly(this.partToken); 3667 } 3668 3669 private TSourceToken commentString; 3670 3671 public void setCommentString(TSourceToken commentString) { 3672 this.commentString = commentString; 3673 } 3674 3675 public TSourceToken getCommentString() { 3676 3677 return commentString; 3678 } 3679 3680 3681 /** 3682 * The X and Y position of this objectName in the SQL 3683 * 3684 * @return coordinate in string text 3685 */ 3686 public String coordinate(){ 3687 return this.getStartToken().lineNo+","+this.getEndToken().columnNo; 3688 } 3689 3690 3691 /** 3692 * @deprecated replaced by {@link EDbObjectType}. 3693 * 3694 * this is not an object, like sysdate function in oracle database 3695 */ 3696 public final static int ttobjNotAObject = -1; 3697 3698 /** 3699 * @deprecated replaced by {@link EDbObjectType}. 3700 * object type can't be determined. 3701 */ 3702 public final static int ttobjUnknown = 0; 3703 3704 /** 3705 * @deprecated replaced by {@link EDbObjectType}. 3706 * column in table, objectToken is table if specified, and partToken is column name. 3707 */ 3708 public final static int ttobjColumn = 1; 3709 3710 /** 3711 * @deprecated replaced by {@link EDbObjectType}. 3712 * column alias in objectToken. 3713 */ 3714 public final static int ttobjColumnAlias = 2; 3715 3716 /** 3717 * @deprecated replaced by {@link EDbObjectType}. 3718 * table name in objectToken. 3719 */ 3720 public final static int ttobjTable = 3; 3721 3722 3723 /** 3724 * @deprecated replaced by {@link EDbObjectType}. 3725 * parameter name in objectToken. 3726 */ 3727 public final static int ttobjParameter = 9; 3728 3729 /** 3730 * @deprecated replaced by {@link EDbObjectType}. 3731 * variable name in objectToken. 3732 */ 3733 public final static int ttobjVariable = 10; 3734 3735 3736 /** 3737 * @deprecated replaced by {@link EDbObjectType#method}. 3738 * column method like SetXY below, column method in {@link #methodToken}, and colomn name in {@link #partToken}. 3739 *<p> UPDATE Cities 3740 *<p> SET Location.SetXY(23.5, 23.5) 3741 * 3742 * 3743 */ 3744 public final static int ttobjColumnMethod = 11; 3745 3746 /** 3747 * Named argument parameter name in function calls. 3748 * <p>Example: In Snowflake FLATTEN(INPUT => parse_json(col), outer => TRUE), 3749 * "INPUT" and "outer" are named argument parameter names, NOT column references. 3750 * <p>These should be skipped during column resolution and data lineage analysis. 3751 */ 3752 public final static int ttobjNamedArgParameter = 12; 3753 3754 /** 3755 * @deprecated replaced by {@link EDbObjectType}. 3756 * function name in {@link #objectToken} 3757 */ 3758 public final static int ttobjFunctionName = 13; 3759 3760 3761 /** 3762 * @deprecated replaced by {@link EDbObjectType#constraint}. 3763 * constraint name in {@link #objectToken} 3764 */ 3765 public final static int ttobjConstraintName = 19; 3766 3767 /** 3768 * @deprecated replaced by {@link EDbObjectType}. 3769 * string constant in {@link #objectToken} 3770 */ 3771 public final static int ttobjStringConstant = 23; 3772 3773 3774 /** 3775 * @deprecated replaced by {@link EDbObjectType}. 3776 * attribute name is in {@link #partToken} 3777 */ 3778 public final static int ttobjAttribute = 26; 3779 3780 3781 /** 3782 * @deprecated replaced by {@link EDbObjectType}. 3783 * datatype was not represented by a TObjectName object, this constant was used in source tokens that consist of TTypeName. 3784 */ 3785 public final static int ttobjDatatype = 30; 3786 3787 /** 3788 * @deprecated replaced by {@link EDbObjectType}. 3789 * schema name in {@link #schemaToken} 3790 */ 3791 public final static int ttobjSchemaName = 31; 3792 3793 3794 /** 3795 * @deprecated replaced by {@link EDbObjectType}. 3796 * postgresql 3797 * Positional Parameters, $1, $1[1], $1[1,10] 3798 * parameter name is in {@link #partToken} of $1, 3799 * and parameter name is in {@link #objectToken} of $1.columnName, 3800 * and column name is in {@link #partToken} 3801 */ 3802 3803 public final static int ttobjPositionalParameters = 61; 3804 3805 3806 3807 private void setColumnTokenOfPositionalParameters(TSourceToken column){ 3808 this.objectToken = this.partToken; 3809 this.partToken = column; 3810 } 3811 3812 private int objectType = ttobjUnknown; 3813 3814 3815 /** 3816 * @deprecated replaced by {@link EDbObjectType}. 3817 * this type is used in TObjectNameList, when objects in TObjectNameList includes more than 3818 * one type, objtype of that TObjectNameList was set to ttobjMixed. 3819 * 3820 * removed since v2.9.2.5 3821 */ 3822 // public final static int ttobjMixed = 100; 3823 3824 /** 3825 * @deprecated replaced by {@link EDbObjectType#library}. 3826 * removed since v2.9.2.5 3827 */ 3828 // public final static int ttObjLibrary = 72; 3829 3830 /** 3831 * @deprecated replaced by {@link EDbObjectType#oracleHint}. 3832 * removed since v2.9.2.5 3833 */ 3834 // public final static int ttObjOracleHint = 70; 3835 3836 /** 3837 * @deprecated replaced by {@link EDbObjectType#fieldName}. 3838 * check {@link gudusoft.gsqlparser.nodes.TExpression#getFieldName()} for more 3839 * removed since v2.9.2.5 3840 */ 3841 // public final static int ttobjFieldName = 51; 3842 3843 /** 3844 * @deprecated replaced by {@link EDbObjectType#miningModel}. 3845 * removed since v2.9.2.5 3846 */ 3847 // public final static int ttobjMiningModel = 46; 3848 3849 /** 3850 * @deprecated replaced by {@link EDbObjectType#materializedView}. 3851 * removed since v2.9.2.5 3852 */ 3853 // public final static int ttobjMaterializedView = 44; 3854 3855 /** 3856 * @deprecated replaced by {@link EDbObjectType#indextype}. 3857 * removed since v2.9.2.5 3858 */ 3859 // public final static int ttobjIndexType = 42; 3860 3861 /** 3862 * @deprecated replaced by {@link EDbObjectType#operator}. 3863 * removed since v2.9.2.5 3864 */ 3865 // public final static int ttobjOperator = 40; 3866 3867 /** 3868 * @deprecated replaced by {@link EDbObjectType#server}. 3869 * server name in {@link #serverToken} 3870 * 3871 * removed since v2.9.2.5 3872 */ 3873 // public final static int ttobjServerName = 32; 3874 3875 /** 3876 * @deprecated replaced by {@link EDbObjectType#sequence}. 3877 * Sequence name in {@link #objectToken} 3878 * 3879 * removed since v2.9.2.5 3880 */ 3881 // public final static int ttobjSequence = 29; 3882 3883 /** 3884 * @deprecated replaced by {@link EDbObjectType#plsql_package}. 3885 * package name in {@link #objectToken} 3886 * 3887 * removed since v2.9.2.5 3888 */ 3889 // public final static int ttobjPackage = 28; 3890 3891 /** 3892 * @deprecated replaced by {@link EDbObjectType#alias}. 3893 * alias name in {@link #objectToken} 3894 * 3895 * removed since v2.9.2.5 3896 */ 3897 // public final static int ttobjAliasName = 25; 3898 3899 3900 /** 3901 * @deprecated replaced by {@link EDbObjectType#trigger}. 3902 * Trigger name in {@link #objectToken} 3903 * 3904 * removed since v2.9.2.5 3905 */ 3906 // public final static int ttobjTrigger = 24; 3907 3908 /** 3909 * @deprecated replaced by {@link EDbObjectType#database}. 3910 * Database name in {@link #objectToken} 3911 * 3912 * removed since v2.9.2.5 3913 */ 3914 // public final static int ttobjDatabaseName = 22; 3915 3916 /** 3917 * @deprecated replaced by {@link EDbObjectType#transaction}. 3918 * Transaction name in {@link #objectToken} 3919 * 3920 * removed since v2.9.2.5 3921 */ 3922 // public final static int ttobjTransactionName = 21; 3923 3924 3925 /** 3926 * @deprecated replaced by {@link EDbObjectType#user_defined_type}. 3927 * type name in {@link #objectToken} 3928 * 3929 * removed since v2.9.2.5 3930 */ 3931 // public final static int ttobjTypeName = 27; 3932 3933 /** 3934 * @deprecated replaced by {@link EDbObjectType#property}. 3935 * property name in {@link #propertyToken} 3936 * 3937 * removed since v2.9.2.5 3938 */ 3939 // public final static int ttobjPropertyName = 20; 3940 3941 /** 3942 * @deprecated replaced by {@link EDbObjectType#view}. 3943 * view name in {@link #objectToken} 3944 * 3945 * removed since v2.9.2.5 3946 */ 3947 // public final static int ttobjViewName = 18; 3948 3949 /** 3950 * @deprecated replaced by {@link EDbObjectType#cursor}. 3951 * cursor name in {@link #objectToken} 3952 * 3953 * removed since v2.9.2.5 3954 */ 3955 // public final static int ttobjCursorName = 17; 3956 3957 /** 3958 * @deprecated replaced by {@link EDbObjectType#materializedView}. 3959 * materialized view name in {@link #objectToken} 3960 * 3961 * removed since v2.9.2.5 3962 */ 3963 // public final static int ttobjMaterializedViewName = 16; 3964 3965 /** 3966 * @deprecated replaced by {@link EDbObjectType#index}. 3967 * index name in {@link #objectToken} 3968 * 3969 * removed since v2.9.2.5 3970 */ 3971 // public final static int ttobjIndexName = 15; 3972 3973 /** 3974 * @deprecated replaced by {@link EDbObjectType#label}. 3975 * label name in {@link #objectToken} 3976 * 3977 * removed since v2.9.2.5 3978 */ 3979 // public final static int ttobjLabelName = 14; 3980 3981 /** 3982 * @deprecated replaced by {@link EDbObjectType#procedure}. 3983 * procedure name in {@link #objectToken} 3984 * 3985 * removed since v2.9.2.5 3986 */ 3987 // public final static int ttobjProcedureName = 12; 3988 3989 /** 3990 * @deprecated replaced by {@link EDbObjectType#variable}. 3991 * table variable in objectToken. 3992 * 3993 * removed since v2.9.2.5 3994 */ 3995 // public final static int ttobjTableVar = 8; 3996 3997 /** 3998 * @deprecated replaced by {@link EDbObjectType#cte}. 3999 * table name in objectToken. 4000 * 4001 * removed since v2.9.2.5 4002 */ 4003 // public final static int ttobjTableCTE = 5; 4004 4005 /** 4006 * @deprecated replaced by {@link EDbObjectType}. 4007 * table name in objectToken. 4008 */ 4009 // public final static int ttobjTableTemp = 6; 4010 4011 /** 4012 * @deprecated replaced by {@link EDbObjectType}. 4013 */ 4014 // public final static int ttobjTablePivot = 7; 4015 4016 /** 4017 * @deprecated replaced by {@link EDbObjectType#table_alias}. 4018 * table alias in objectToken 4019 * 4020 * removed since v2.9.2.5 4021 */ 4022 // public final static int ttObjTableAlias = 4; 4023}