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