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