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