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