001package gudusoft.gsqlparser.nodes;
002
003import gudusoft.gsqlparser.*;
004import gudusoft.gsqlparser.compiler.TStmtScope;
005import gudusoft.gsqlparser.nodes.hive.THiveKeyValueProperty;
006import gudusoft.gsqlparser.nodes.mssql.TForXMLClause;
007import gudusoft.gsqlparser.nodes.snowflake.TAtBeforeClause;
008import gudusoft.gsqlparser.nodes.snowflake.TStageReference;
009import gudusoft.gsqlparser.nodes.teradata.THashByClause;
010import gudusoft.gsqlparser.nodes.teradata.TTDUnpivot;
011import gudusoft.gsqlparser.resolver.TResolverHelpUtils;
012import gudusoft.gsqlparser.sqlenv.ESQLDataObjectType;
013import gudusoft.gsqlparser.sqlenv.TSQLEnv;
014import gudusoft.gsqlparser.sqlenv.TSQLTable;
015import gudusoft.gsqlparser.util.SQLUtil;
016import gudusoft.gsqlparser.stmt.TMergeSqlStatement;
017import gudusoft.gsqlparser.stmt.TSelectSqlStatement;
018import gudusoft.gsqlparser.stmt.hive.THiveFromQuery;
019
020import java.util.ArrayList;
021
022/**
023 * Represents various kinds of table source in from clause. Can also be a simple table/view name in create table and all other places.
024 * result of {@link #getTableType} can be one of:
025 *<ul>
026 * <li>ftt_objectname, in from clause, a simple table/view name, reference: {@link TTable#tableName}<li>
027 * <li>ftt_subquery, is a subquery that retrieves rows from the database, also known as derived table. reference: {@link TTable#subquery}</li>
028 * <li>ftt_tableExpr,it's usually a table-valued expression., reference: {@link TTable#tableExpr}</li>
029 * <li>ftt_function, it's usually a table-valued function., reference: {@link TTable#funcCall}</li>
030 * <li>{@link ETableSource#rowList}, it's constructed rows, reference: {@link TTable#rowList}</li>
031 * <li>ftt_containsTable, CONTAINSTABLE clause of sql server. reference: {@link TTable#containsTable}, type of {@link TContainsTable}</li>
032 * <li>ftt_freetextTable, FREETEXTTABLE  clause of sql server. reference: {@link TTable#containsTable}, type of {@link TContainsTable}</li>
033 * <li>ftt_openrowset, OPENROWSET clause of sql server. reference: {@link TTable#openRowSet}, type of {@link TOpenRowSet}</li>
034 * <li>ftt_openxml, OPENXML clause of sql server. reference: {@link TTable#openXML}, type of {@link TOpenXML }</li>
035 * <li>ftt_opendatasource, OPENDATASOURCE clause of sql server. reference: {@link TTable#openDatasource}, type of {@link TOpenDatasource}</li>
036 * <li>ftt_openquery, OPENQUERY clause of sql server. reference: {@link TTable#openquery}, type of (@link TOpenQuery)</li>
037 * </ul>
038 *
039 *
040*/
041
042public class TTable extends TNodeWithAliasClause implements IRelation {
043    private ArrayList<TAttributeNode> relationAttributes;
044
045    /**
046     * @deprecated ,  this method is deprecated since version 3.3.1.0, don't use it in TSQLResolver2
047     * @return
048     */
049    @Override
050    public ArrayList<TAttributeNode> getAttributes(){
051        if (relationAttributes == null) {
052            relationAttributes = new ArrayList<>();
053        }
054        return relationAttributes;
055    }
056
057    @Override
058    public String getRelationName(){
059        return this.getName();
060    }
061
062    @Override
063    public int size(){
064        return relationAttributes != null ? relationAttributes.size() : 0;
065    }
066
067    public void setStageReference(TStageReference stageReference) {
068        this.stageReference = stageReference;
069    }
070
071    private TStageReference stageReference;
072
073    public TStageReference getStageReference() {
074        return stageReference;
075    }
076    private TAtBeforeClause timeTravelClause;
077
078    public void setTimeTravelClause(TAtBeforeClause timeTravelClause) {
079        this.timeTravelClause = timeTravelClause;
080    }
081
082    public TAtBeforeClause getTimeTravelClause() {
083        return timeTravelClause;
084    }
085
086    private TCaseJoinClause caseJoin;
087
088    public void setCaseJoin(TCaseJoinClause caseJoin) {
089        this.caseJoin = caseJoin;
090    }
091
092    public TCaseJoinClause getCaseJoin() {
093        return caseJoin;
094    }
095
096    public String getStageName(){
097        if (this.getTableName() == null) return null;
098        if (this.getTableName().getDbObjectType() != EDbObjectType.stage) return null;
099        return this.getTableName().getObjectString();
100    }
101    public final static String PIVOT_CLAUSE_ALIAS = "(pivot_table)";
102    public final static String UNPIVOT_CLAUSE_ALIAS = "(unpivot_table)";
103    public final static String TABLE_COLLECTION_ALIAS = "(table_collection)";
104
105
106    boolean isAttributesInitialized = false;
107
108    /**
109     * Initialize a star attribute node (tableName.*) for this table.
110     * This is used for regular tables (without DDL/metadata) in contexts where
111     * they need to compete with subqueries that have star columns.
112     * For example, in MERGE statements, the target table needs a star attribute
113     * so that unqualified columns in the ON clause can be resolved to it
114     * instead of being incorrectly pushed down through the source subquery's star column.
115     */
116    public void initStarAttribute(){
117        if (isAttributesInitialized) return;
118        TAttributeNode.addNodeToList(new TAttributeNode( getDisplayName()+".*",this),getAttributes());
119        isAttributesInitialized = true;
120    }
121
122    public void initAttributeForXMLTable(){
123       // if (isAttributesInitialized) return;
124            //relationAttributes.add(new TAttributeNode( getDisplayName()+".*",this));
125        TAttributeNode.addNodeToList(new TAttributeNode( getDisplayName()+".*",this),getAttributes());
126        isAttributesInitialized = true;
127    }
128
129    public void initAttributeForTableFunction(){
130       // if (isAttributesInitialized) return;
131           //relationAttributes.add( new TAttributeNode( getDisplayName()+".value",this));
132        TAttributeNode.addNodeToList(new TAttributeNode( getDisplayName()+".value",this),getAttributes());
133        if (this.getName().equals("STRING_SPLIT")){ 
134            // TRING_SPLIT函数生成的表只有两个字段:value和可选的ordinal
135            TAttributeNode.addNodeToList(new TAttributeNode( getDisplayName()+".ordinal",this),getAttributes());
136        }
137        isAttributesInitialized = true;
138    }
139
140    public void initAttributeForRowList(){
141       // if (isAttributesInitialized) return;
142       // relationAttributes.add( new TAttributeNode( getDisplayName()+".value",this));
143        if (getValueClause() == null) return;
144        if (getValueClause().getRows().size() == 0) return;
145
146        int i = 0;
147        String columnName = "";
148        TObjectNameList aliasColumnNameList = null;
149        // 1. 如果有 table alias 中指定 column, 遍历alias column
150        // 2. 如果 table alias 没有指定column, 遍历 subquery or  values() 中的 resultcolumn
151        // 如果遍历alias column,可能存在 alias column 中column 数量和  subquery or  values() 中的 resultcolumn 中不等的现象
152        // 例如 subquery or  values() 中的 resultcolumn 中为 * column,
153        // 或者 snowflake 中的 pivot clause.(c:\prg\gsp_sqlfiles\TestCases\private\dataflow\snowflake\pivot_clause_1.sql)
154
155        TAliasClause aliasClause = this.getAliasClause();
156        if (aliasClause != null){
157            aliasColumnNameList = aliasClause.getColumns();
158        }
159        for(TResultColumn resultColumn:getValueClause().getRows().get(0)){
160            columnName = resultColumn.getDisplayName();
161            if ((aliasColumnNameList != null)&&(aliasColumnNameList.size()>i)){
162                columnName = aliasColumnNameList.getObjectName(i).getColumnNameOnly();
163            }
164            TAttributeNode newNode = new TAttributeNode(this.getDisplayName() + "." + columnName, this, resultColumn);
165            //newNode.setAttributeCreatedFromAliasColumn(true);
166            //relationAttributes.add(newNode);
167            TAttributeNode.addNodeToList(newNode,relationAttributes);
168            i++;
169        }
170
171        TBaseType.log(String.format("Prepare attributes (num = %d) for rowList (values) : <%s>",this.getAttributes().size() ,this.getDisplayName()),TLog.DEBUG,this.getTableName());
172        int c = 0;
173        for(TAttributeNode node: this.getAttributes()){
174            TBaseType.log(String.format("\tAttribute: <%s>, result column: <%s>",node.getName(), node.getSubLevelResultColumn()!=null?node.getSubLevelResultColumn().toString():"N/A" ),TLog.DEBUG);
175            c++;
176            if (c > TLog.OUTPUT_ATTRIBUTES_MAX){
177                TBaseType.log(String.format("\t...skipped after output %d attributes",c-1),TLog.DEBUG,this.getTableName().getStartToken());
178                break;
179            }
180        }
181
182
183        isAttributesInitialized = true;
184    }
185
186
187    public void initAttributesFromCTE(TCTE cte){
188       // if (isAttributesInitialized) return;
189
190        for (TAttributeNode node1 : cte.getAttributes()) {
191            TAttributeNode newNode = new TAttributeNode(this.getDisplayName() + "." + node1.getLastPartOfName(), this, node1.getSqlColumn(), node1.getSubLevelResultColumn());
192            if (node1.getAccompaniedAttributeNodes().size() > 0){
193                for(TAttributeNode n:node1.getAccompaniedAttributeNodes()){
194                    newNode.getAccompaniedAttributeNodes().add(n);
195                }
196            }
197            //relationAttributes.add(newNode);
198            TAttributeNode.addNodeToList(newNode,relationAttributes);
199        }
200
201        TBaseType.log(String.format("Prepare attributes (num = %d) for cte ref: <%s>",this.getAttributes().size() ,this.getTableName().toString()),TLog.DEBUG,this.getTableName());
202        int c = 0;
203        for(TAttributeNode node: this.getAttributes()){
204            if (node.getSqlColumn() != null){
205                TBaseType.log(String.format("\tAttribute: <%s>, SQL column: <%s>",node.getName(), node.getSqlColumn()!=null?node.getSqlColumn().toString():"N/A" ),TLog.DEBUG);
206            }else{
207                TBaseType.log(String.format("\tAttribute: <%s>, Select list column: <%s>",node.getName(), node.getSubLevelResultColumn()!=null?node.getSubLevelResultColumn().toString():"N/A" ),TLog.DEBUG);
208            }
209            c++;
210            if (c > TLog.OUTPUT_ATTRIBUTES_MAX){
211                TBaseType.log(String.format("\t...skipped after output %d attributes",c-1),TLog.DEBUG,this.getTableName().getStartToken());
212                break;
213            }
214        }
215
216        isAttributesInitialized = true;
217    }
218    public void  initAttributesFromSubquery(TSelectSqlStatement subquery, String prefix){
219        // TODO, not thread safe?
220      //  if ((isAttributesInitialized) && (!relationAttributes.isEmpty())) return;
221        if (subquery.getResultColumnList() == null) return;
222        int i = 0;
223        if (subquery.getResultColumnList() != null) {
224            TResultColumnList resultColumnListFromSubQuery = subquery.getResultColumnList(true);
225            for(TResultColumn column: resultColumnListFromSubQuery){
226                if (column.getAliasNameList().size() > 0){
227                    // 如果 column 有 alias name list, 则遍历 alias name list, 并把每个 alias name 作为 attribute node 添加到 relationAttributes 中
228                    // as (b1, b2, b3, b4, b5)
229                    // select t.a1, t.a2, stack(2, 'T', t.a1, t.a2, t.a3/t.a4, t.a5/t.a6,   'T+0', t.a7, t.a8, t.a9/t.a10, t.a11/t.a12) as (b1, b2, b3, b4, b5)  from db1.table1 t
230
231                    for(TObjectName aliasName: column.getAliasNameList()){
232                        this.addAttribute(new TAttributeNode(prefix + TBaseType.removeQuoteChar(aliasName.toString()),this,column,i));
233                        i++;
234                    }
235                }else {
236                    if (column.getDisplayName().endsWith("*")){
237                        TObjectName objectName = column.getExpr().getObjectOperand();
238                        if (objectName.getAttributeNodesDerivedFromFromClause().size() > 0){
239                            for(TAttributeNode attributeNode:objectName.getAttributeNodesDerivedFromFromClause()){
240                                //relationAttributes.add(new TAttributeNode( prefix + attributeNode.getLastPartOfName() ,this, attributeNode.getSqlColumn(), column,i));
241                                this.addAttribute(new TAttributeNode( prefix + attributeNode.getLastPartOfName() ,this, attributeNode.getSqlColumn(), column,i));
242                            }
243                        }else{
244                            // no attribute node derived from from clause, add the object name directly
245                            //relationAttributes.add(new TAttributeNode(prefix +"*",this,column,i));
246                            this.addAttribute(new TAttributeNode(prefix +"*",this,column,i));
247                        }
248                    }else{
249                        //relationAttributes.add(new TAttributeNode(prefix +column.getDisplayName(),this,column,i));
250                        this.addAttribute(new TAttributeNode(prefix +column.getDisplayName(),this,column,i));
251                    }
252                    i++;
253                }
254            }
255        }
256
257        //  如果 subQuery 是 combined query, 需要把所有 query 中的 select list 都加入进来。上面处理的仅仅为最右面的那个 subQuery
258        if (subquery.isCombinedQuery()){
259            TSelectSqlStatement left = subquery.getLeftStmt();
260            addNewAttributeFromSubQuery(left.getResultColumnList(true),getAttributes(),prefix);
261            while (left.isCombinedQuery()){
262                left = left.getLeftStmt();
263                addNewAttributeFromSubQuery(left.getResultColumnList(true),getAttributes(),prefix);
264            }
265        }
266        isAttributesInitialized = true;
267    }
268
269    void addNewAttributeFromSubQuery(TResultColumnList resultColumnList, ArrayList<TAttributeNode> attributeNodes, String prefix){
270        addNewAttributeFromSubQuery(resultColumnList,attributeNodes,prefix,null);
271    }
272
273    /**
274     * 当 subQuery 为 combined query 时,不仅仅把 right most 那个 subQuery 的 select list 作为 子查询的 attribute node list 添加上来
275     * 还需要把 其他 combined query 中的 subQuery 加入进来
276     *
277     *
278     *
279     * @param resultColumnList  subQuery 中的 select list
280     * @param attributeNodes  需要在该 attribute node 的 getAccompaniedAttributeNodes() 中添加新 node
281     * @param prefix  该前缀为 subQuery 的 alias,或 CTE 的 name 决定
282     * @param specifiedColumnList 当 table 为 CTE 时,并且 CTE 自己指定了 column list, 那么 attribute node 的 name 由该 column list 决定,
283     *                            而不是 subQuery 中的 select list 确定
284     *
285     */
286    void addNewAttributeFromSubQuery(TResultColumnList resultColumnList, ArrayList<TAttributeNode> attributeNodes, String prefix, TObjectNameList specifiedColumnList){
287        if (resultColumnList == null) return;
288        if (resultColumnList.size() !=  attributeNodes.size()){ 
289            if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
290                TBaseType.log(String.format("When add attribute from subQuery, the size of select list and attributes is not the same: %d <> %d",resultColumnList.size(),attributeNodes.size())
291                        ,TLog.ERROR,resultColumnList.getStartToken());
292            }
293            return;
294        }
295        int i = 0;
296        for(TResultColumn column: resultColumnList){
297            TAttributeNode node = attributeNodes.get(i); // 根据 i 的值,找到原来对应位置的 attribute node,用来添加 accompanied node
298
299            if (column.getDisplayName().endsWith("*")){
300                TObjectName objectName = column.getExpr().getObjectOperand();
301                for(TAttributeNode attributeNode:objectName.getAttributeNodesDerivedFromFromClause()){
302                    if (specifiedColumnList == null){
303                        node.getAccompaniedAttributeNodes().add(new TAttributeNode( prefix + attributeNode.getLastPartOfName() ,this, attributeNode.getSqlColumn(), column,i));
304                    }else{
305                        TObjectName c = specifiedColumnList.getObjectName(i);
306                        node.getAccompaniedAttributeNodes().add(new TAttributeNode( prefix + c.toString() ,this, attributeNode.getSqlColumn(), column,i));
307                    }
308                }
309            }else{
310                if (specifiedColumnList == null){
311                    node.getAccompaniedAttributeNodes().add(new TAttributeNode(prefix +column.getDisplayName(),this,column,i));
312                }else{
313                    TObjectName c = specifiedColumnList.getObjectName(i);
314                    node.getAccompaniedAttributeNodes().add(new TAttributeNode(prefix + c.toString(),this,column,i));
315                }
316            }
317            i++;
318        }
319    }
320
321    public String getDisplayName(){
322        if (getAliasClause() != null) return getAliasName();
323        String ret = null;
324        switch (getTableType()){
325            case objectname:
326                ret = getFullName();
327                break;
328            case tableExpr:
329                ret = TTable.TABLE_COLLECTION_ALIAS;
330                break;
331            default:
332                ret = toString();
333                break;
334        }
335        return ret;
336    }
337
338    public String getDisplayName(boolean removeDBLink){
339        if (!removeDBLink) return getDisplayName();
340        if (dbvendor != EDbVendor.dbvoracle) return getDisplayName();
341        // DM_MINING.FATURA_ODEME_SEGMENTASYONU@dblinkx
342        String ret = getDisplayName();
343        int atIndex = ret.indexOf('@');
344        if (atIndex > 0){
345            ret = ret.substring(0,atIndex);
346        }
347        return ret;
348    }
349
350
351    /**
352     * 在 sql script 中,和该 relation 关联的 attribute。
353     * select a from t;
354     * attribute a 就是 relation t 的 referenceAttribute, 如果没有 metadata and ddl,
355     * relation t 的 relationAttributes 应该可以推断出包含 a, 但 t 是否还包含其他的 attribute 就无从得知。
356     */
357    ArrayList<TObjectName> referenceAttributes = new ArrayList<>();
358
359//    public ArrayList<TObjectName> getReferenceAttributes(){
360//        return null;
361//    }
362
363    public void initAttributesForPivotTable(){
364       // if (isAttributesInitialized) return;
365        TPivotClause pivotClause = this.getPivotedTable().getPivotClauseList().getElement(0);
366        if (pivotClause.getType() == TPivotClause.pivot){
367
368            // 先添加 pivot clause 中的 attribute,以防止 先处理 [p].[3] -> p.*, 而导致 [p].[3] -> [p].[3] 没能正确匹配
369
370            // SELECT [p].[ADDRESS_ID]
371            //                      ,[p].[3] AS ADDRESS_LINE3
372            //                      ,[p].[2] AS ADDRESS_LINE2
373            //                      ,[p].[1] AS ADDRESS_LINE1 FROM [ADDRESS_LINES_CTE]
374            //                          PIVOT(MAX(value) FOR id IN ([1],[2],[3])) p
375
376            //relationAttributes.addAll(pivotClause.getAttributes());
377            TAttributeNode.addAllNodesToList(pivotClause.getAttributes(),getAttributes());
378
379            TTable sourceTable = this.getPivotedTable().getRelations().get(0);
380            // 如果 pivot alias clause 不为空,整个table的 prefix 都用这个alias
381            //  否则使用当前table的display name
382            String prefix;
383            if (pivotClause.getAliasClause() != null){
384                prefix = pivotClause.getDisplayName();
385            }else{
386                prefix = sourceTable.getDisplayName();
387            }
388
389            for(TAttributeNode node: sourceTable.getAttributes()){
390                    //relationAttributes.add(new TAttributeNode(prefix+"."+ TBaseType.getLastPartOfQualifiedName(node.getName()),sourceTable,node.getSubLevelResultColumn()));
391                TAttributeNode.addNodeToList(new TAttributeNode(prefix+"."+ TBaseType.getLastPartOfQualifiedName(node.getName()),sourceTable,node.getSubLevelResultColumn()),getAttributes());
392            }
393
394
395        }
396
397        isAttributesInitialized = true;
398    }
399
400    /**
401     * 主要是在处理列级别的信息,
402     * 它的主要目的是确保 JOIN 操作后的表对象包含所有参与 JOIN 的表的列信息,这对于后续的查询处理(特别是列引用解析)是必要的。
403     */
404    public void initAttributesForJoin(){
405        //if (isAttributesInitialized) return;
406        //relationAttributes.addAll(getJoinExpr().getAttributes());
407        TAttributeNode.addAllNodesToList(getJoinExpr().getAttributes(),getAttributes());
408        isAttributesInitialized = true;
409    }
410
411    public void initAttributesForUnnest(TSQLEnv sqlEnv, TSelectSqlStatement select ){
412       // if (isAttributesInitialized) return;
413        if (this.getAliasClause() != null){
414            if (this.getAliasClause().getColumns() != null){
415                for(TObjectName pColumn:this.getAliasClause().getColumns()){
416                       //relationAttributes.add( new TAttributeNode( this.getDisplayName()+"."+pColumn.toString() ,this));
417                    TAttributeNode.addNodeToList(new TAttributeNode( this.getDisplayName()+"."+pColumn.toString() ,this),getAttributes());
418                }
419            }else if (this.getAliasClause().getAliasName() != null){
420//                       SELECT *
421//                               FROM UNNEST(['foo', 'bar', 'baz', 'qux', 'corge', 'garply', 'waldo', 'fred']) AS element
422//                       WITH OFFSET AS offset
423                // add element as column of unnest table.
424                if (this.getUnnestClause().getColumns() != null){
425                    //   UNCOVERED_MOVE_Stage1 AS
426                    //  (SELECT
427                    //     Instrument_Partition_Key ,
428                    //     Consolidation_Entity_Levels ,
429                    //     FOTC_UDF_calc_uncovered_roll_off(ARRAY_AGG(STRUCT<Partition_Key STRING , Order_Number INT64 , Movement_In_Short FLOAT64 , Short_Increase_Uncovered FLOAT64 , Short_Increase_Covered_CS FLOAT64 , Short_Increase_Covered_RR FLOAT64> (Instrument_Partition_Key , Order_Number , Movement_In_Short_Balance_LCY , Short_Increase_Uncovered , Maturing_CS_Covering_Short_Increase , Maturing_RR_Covering_Short_Increase)
430                    //                                                ORDER BY Order_Number)) AS result_uncovered_moves ,
431                    //     FOTC_UDF_calc_uncovered_roll_off(ARRAY_AGG(STRUCT<Partition_Key STRING , Order_Number INT64 , Movement_In_Short FLOAT64 , Short_Increase_Uncovered FLOAT64 , Short_Increase_Covered_CS FLOAT64 , Short_Increase_Covered_RR FLOAT64> (Instrument_Partition_Key , Order_Number , Movement_In_Short_Balance_LCY , Short_Increase_Uncovered_By_Settled_RR , Maturing_CS_Covering_Short_Increase , Maturing_Settled_RR_Covering_Short_Increase)
432                    //                                                ORDER BY Order_Number)) AS result_uncovered_by_settled_moves
433                    //   FROM SHORTS_COVERING_RR
434                    //   GROUP BY
435                    //     Instrument_Partition_Key ,
436                    //     Consolidation_Entity_Levels) ,
437                    //  UNCOVERED_MOVE AS
438                    //  (SELECT
439                    //     Consolidation_Entity_Levels ,
440                    //     X.* ,
441                    //     Y.uncovered_move AS Move_Uncovered_By_Settled_RR_Short ,
442                    //     Y.covered_cs_move AS Move_Covered_By_Settled_CS_Short ,
443                    //     Y.covered_rr_move AS Move_Covered_By_Settled_RR_Short
444                    //   FROM
445                    //     UNCOVERED_MOVE_Stage1 ,
446                    //     UNNEST(result_uncovered_moves) AS X
447                    //   JOIN UNNEST(result_uncovered_by_settled_moves) AS Y ON X.Partition_Key = Y.Partition_Key
448                    //   AND X.order_key= Y.order_key)
449
450                    // UNNEST(result_uncovered_moves) 中的 result_uncovered_moves 来自 ARRAY_AGG(),
451                    // 这时候因为还没有进行 column resolver,所以无法知道 result_uncovered_moves 到底包含哪些 column
452                    // 因为 UNNEST(result_uncovered_moves) AS X 指定了 X 作为 Alias, 因此增加 X.* 作为 attributeNode
453                    // 不会导致问题。
454
455                    ArrayList<String> tf = null;
456                    TObjectName objectName = this.getUnnestClause().getColumns().getObjectName(0);
457                    for(TTable table: select.getRelations()){
458                        if (TParseTreeNode.subNodeInNode(this,table)) break;
459                        for(TAttributeNode attributeNode:table.getAttributes()){
460                            int matchResult = TStmtScope.matchAttribute(objectName.toString(), attributeNode.getName(),0,select);
461                            if (matchResult == TBaseType.MATCH_COLUMN_RESULT_MATCHED ) {
462                                objectName.setSourceTableBySQLResolver(select,attributeNode,table);
463                                objectName.setSourceAttributeNode(attributeNode);
464                                break;
465                            }
466                        }
467                        if (objectName.getSourceAttributeNode() != null){
468                            if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
469                                TBaseType.log(String.format("Unnest table from source column: <%s>, linked to attribute node: <%s>"
470                                        ,objectName.toString(),objectName.getSourceAttributeNode().getName()),TLog.DEBUG,objectName);
471                            }
472
473                            // unnest(column), 其中的 column 可能对应array, 或者是 struct
474                            // 找一下看看是否有 struct
475
476                             tf = TResolverHelpUtils.searchTypedStruct(sqlEnv, objectName.getSourceAttributeNode().getSubLevelResultColumn());
477                            break;
478                        }
479                    }
480                    if (tf != null){
481                        if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
482                            TBaseType.log(String.format("Prepare attribute node for unnest %s",this.getDisplayName()),TLog.DEBUG,this.getStartToken());
483                        }
484                        for(String columnName:tf){
485                            TAttributeNode newNode = new TAttributeNode(String.format("%s.%s",this.getDisplayName(),columnName),this,objectName.getSourceAttributeNode().getSubLevelResultColumn());
486                            //relationAttributes.add(newNode);
487                            TAttributeNode.addNodeToList(newNode,relationAttributes);
488                            if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
489                                TBaseType.log(String.format("\t attribute node from struct: %s.%s with source column: %s"
490                                            , this.getDisplayName(),columnName, objectName.getSourceAttributeNode().getSubLevelResultColumn()==null?"N/A":objectName.getSourceAttributeNode().getSubLevelResultColumn().toString() ),TLog.DEBUG,this.getStartToken());
491                            }
492                        }
493                    }else{
494                           //relationAttributes.add( new TAttributeNode( this.getDisplayName()+".*",this));
495                        TAttributeNode.addNodeToList(new TAttributeNode( this.getDisplayName()+".*",this),getAttributes());
496                    }
497
498                }else{
499                       //relationAttributes.add( new TAttributeNode( this.getDisplayName()+"."+this.getAliasClause().getAliasName().toString() ,this));
500                    TAttributeNode.addNodeToList(new TAttributeNode( this.getDisplayName()+"."+this.getAliasClause().getAliasName().toString() ,this),getAttributes());
501                }
502            }
503        }else{
504            //SELECT value FROM UNNEST(nested_attribute)
505            // #TODO, 未处理 UNNEST 中的 nested_attribute,  nested_attribute 一般是一个 struct,那么应该把 struct 中的 column 也加入进来
506            // 没有指定输出 column 的name,默认为 value
507               //relationAttributes.add( new TAttributeNode( this.getDisplayName()+"."+"value" ,this));
508            TAttributeNode.addNodeToList(new TAttributeNode( this.getDisplayName()+"."+"value" ,this),getAttributes());
509        }
510
511        for(int i=0;i<getAttributes().size();i++){
512            TAttributeNode node = getAttributes().get(i);
513            if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
514                TBaseType.log(String.format("Prepare attribute node <%d> for unnest %s: %s",i+1,this.getDisplayName(),node.getName()),TLog.DEBUG,this.getStartToken());
515            }
516        }
517
518        isAttributesInitialized = true;
519    }
520
521   
522
523
524//    private ArrayList<String> expandedAttributes = new ArrayList<>();
525//
526//    public ArrayList<String> getExpandedAttributes() {
527//        return expandedAttributes;
528//    }
529
530
531    public void addAttribute(TAttributeNode node) {
532        if (node == null) return;
533        
534        // 检查是否已存在同名节点
535        for (TAttributeNode existing : getAttributes()) {
536            if (existing.getName().equals(node.getName())) {
537                return;
538            }
539        }
540        
541        //relationAttributes.add(node);
542        TAttributeNode.addNodeToList(node,relationAttributes);
543    }
544
545    private ArrayList<TObjectName> attributesReferenceToThisRelation = new ArrayList<>();
546
547    public ArrayList<TObjectName> getAttributesReferenceToThisRelation() {
548        return attributesReferenceToThisRelation;
549    }
550
551//    public boolean resolveAttribute(TObjectName attribute){
552//        boolean result = false;
553//        if (attribute.isQualified()) {
554//            result = attribute.resolveWithThisTable(this);
555//            if (result) {
556//                attributesReferenceToThisRelation.add(attribute);
557//                attribute.setResolvedRelation(this);
558//            }
559//            return result;
560//        }
561//        if (this.getResolvedTable() != null){
562//            result = this.getResolvedTable().searchColumn(attribute.toString());
563//            if (result){
564//                attribute.setResolvedRelation(this);
565//                return result;
566//            }
567//        }
568//        return result;
569//    }
570
571    private TSQLTable resolvedTable = null;
572    private boolean isResolved = false;
573    private ETableCatalogStatus catalogStatus = ETableCatalogStatus.NOT_CHECKED;
574
575    public void setResolvedTable(TSQLTable resolvedTable) {
576        this.resolvedTable = resolvedTable;
577        isResolved = true;
578        // A catalog entry in hand is FOUND by definition. A null says nothing on its own: the
579        // caller that did the lookup knows whether there was metadata to look in, and says so
580        // through setCatalogStatus.
581        if (resolvedTable != null) {
582            catalogStatus = ETableCatalogStatus.FOUND;
583        }
584    }
585
586    /**
587     * Whether this table reference was found in the catalog metadata. A fact about the table,
588     * separate from which table a column belongs to: see {@link ETableCatalogStatus}.
589     */
590    public ETableCatalogStatus getCatalogStatus() {
591        return catalogStatus;
592    }
593
594    public void setCatalogStatus(ETableCatalogStatus catalogStatus) {
595        this.catalogStatus = catalogStatus == null ? ETableCatalogStatus.NOT_CHECKED : catalogStatus;
596    }
597
598    public void setResolved(boolean resolved) {
599        isResolved = resolved;
600    }
601
602    public TSQLTable getResolvedTable() {
603        return resolvedTable;
604    }
605
606    public boolean isResolved() {
607        return isResolved;
608    }
609
610    private ArrayList<TObjectName> columnsFromSQLEnv;
611
612    public ArrayList<TObjectName> getColumnsFromSQLEnv(TSQLEnv sqlEnv) {
613        if (columnsFromSQLEnv == null){
614            columnsFromSQLEnv = new ArrayList<>();
615        }
616
617        ArrayList<String> columns = sqlEnv.getColumnsInTable(this.tableName.toString(),true);
618        if (columns == null) return null;
619
620        for(String s:columns){
621            TObjectName column = TObjectName.createObjectName(this.dbvendor, EDbObjectType.column,new TSourceToken(s));
622            columnsFromSQLEnv.add(column);
623        }
624
625        return columnsFromSQLEnv;
626    }
627
628    private TJoinExpr joinExpr;
629
630    public void setJoinExpr(TJoinExpr joinExpr) {
631        this.joinExpr = joinExpr;
632    }
633
634    public TJoinExpr getJoinExpr() {
635        return joinExpr;
636    }
637
638    /**
639     *  该 table 对应的 sqlenv 中的 table 定义,包含具体的字段信息,有字段名称、数据类型等。
640     *
641     */
642//    private TSQLTable sqlTable;
643//
644//    public void setSqlTable(TSQLTable sqlTable) {
645//        this.sqlTable = sqlTable;
646//    }
647//
648//    public TSQLTable getSqlTable() {
649//        return sqlTable;
650//    }
651
652
653
654    private TForXMLClause forXMLClause;
655
656    public void setForXMLClause(TForXMLClause forXMLClause) {
657        this.forXMLClause = forXMLClause;
658    }
659
660    /**
661     * SQL Server for xml clause
662     * @return SQL Server for xml clause
663     */
664    public TForXMLClause getForXMLClause() {
665        return forXMLClause;
666    }
667
668    private TColumnDefinitionList columnDefinitions;
669
670    public TColumnDefinitionList getColumnDefinitions() {
671        return columnDefinitions;
672    }
673
674    /** Input correlation name inside a Teradata table operator's ON clause. */
675    private TAliasClause tableOperatorInputAlias;
676    /** Partition expressions over the ON input, separate from its output alias. */
677    private TPartitionClause tableOperatorPartitionBy;
678    /** Ordering of rows supplied to a Teradata table operator. */
679    private TOrderBy tableOperatorOrderBy;
680
681    public TAliasClause getTableOperatorInputAlias() { return tableOperatorInputAlias; }
682    public void setTableOperatorInputAlias(TAliasClause value) { tableOperatorInputAlias = value; }
683    public TPartitionClause getTableOperatorPartitionBy() { return tableOperatorPartitionBy; }
684    public void setTableOperatorPartitionBy(TPartitionClause value) { tableOperatorPartitionBy = value; }
685    public TOrderBy getTableOperatorOrderBy() { return tableOperatorOrderBy; }
686    public void setTableOperatorOrderBy(TOrderBy value) { tableOperatorOrderBy = value; }
687
688    /**
689     * Teradata HASH BY clause for table functions.
690     */
691    private THashByClause hashByClause;
692
693    /**
694     * Gets the HASH BY clause for Teradata table functions.
695     *
696     * @return the HASH BY clause, or null if not specified
697     */
698    public THashByClause getHashByClause() {
699        return hashByClause;
700    }
701
702    /**
703     * Sets the HASH BY clause for Teradata table functions.
704     *
705     * @param hashByClause the HASH BY clause
706     */
707    public void setHashByClause(THashByClause hashByClause) {
708        this.hashByClause = hashByClause;
709    }
710
711    /**
712     * Teradata LOCAL ORDER BY clause for table functions.
713     */
714    private TOrderBy localOrderBy;
715
716    /**
717     * Gets the LOCAL ORDER BY clause for Teradata table functions.
718     *
719     * @return the LOCAL ORDER BY clause, or null if not specified
720     */
721    public TOrderBy getLocalOrderBy() {
722        return localOrderBy;
723    }
724
725    /**
726     * Sets the LOCAL ORDER BY clause for Teradata table functions.
727     *
728     * @param localOrderBy the LOCAL ORDER BY clause
729     */
730    public void setLocalOrderBy(TOrderBy localOrderBy) {
731        this.localOrderBy = localOrderBy;
732    }
733
734    private TSQLEnv sqlEnv;
735
736    public void setSqlEnv(TSQLEnv sqlEnv) {
737        this.sqlEnv = sqlEnv;
738    }
739
740    public TSQLEnv getSqlEnv() {
741        return sqlEnv;
742    }
743
744    private TValueClause valueClause;
745
746    public TValueClause getValueClause() {
747        return valueClause;
748    }
749
750    public void setValueClause(TValueClause valueClause) {
751        this.valueClause = valueClause;
752    }
753
754    private TTable sourceTableOfPivot;
755
756    public void setSourceTableOfPivot(TTable sourceTableOfPivot) {
757        this.sourceTableOfPivot = sourceTableOfPivot;
758    }
759
760    public TTable getSourceTableOfPivot() {
761        return sourceTableOfPivot;
762    }
763
764    private TJsonTable jsonTable;
765
766    public void setJsonTable(TJsonTable jsonTable) {
767        this.jsonTable = jsonTable;
768    }
769
770    public TJsonTable getJsonTable() {
771        return jsonTable;
772    }
773
774    public void  setPropertyFromObjectName(TObjectName objectName, ETableEffectType tableEffectType){
775        setStartToken(objectName.getStartToken());
776        setEndToken(objectName.getEndToken());
777        setGsqlparser(objectName.getGsqlparser());
778        setEffectType(tableEffectType);
779        setTableType(ETableSource.objectname);
780    }
781    private TTDUnpivot tdUnpivot;
782
783    public void setTdUnpivot(TTDUnpivot tdUnpivot) {
784        this.tdUnpivot = tdUnpivot;
785    }
786
787    public TTDUnpivot getTdUnpivot() {
788
789        return tdUnpivot;
790    }
791
792    private TUnnestClause unnestClause;
793
794    public void setUnnestClause(TUnnestClause unnestClause) {
795        this.unnestClause = unnestClause;
796    }
797
798    public TUnnestClause getUnnestClause() {
799
800        return unnestClause;
801    }
802    //select b.TemaTakipNo, b.SevkID, db.FromDepo, db.ToDepo, d.UrunID UrunID1, r.UrunID2, d.Miktar AsortiMiktar, r.Miktar ReceteMiktar
803    //into #tmpIrsaliye
804    /**
805     * list of columns in select list, used as metadata to determine whether a column belong to this temp table in TCustomSqlStatement.linkColumnToTable
806     */
807    private TResultColumnList columnListInTempTable;
808
809    public void setColumnListInTempTable(TResultColumnList columnListInTempTable) {
810        this.columnListInTempTable = columnListInTempTable;
811    }
812
813    public TResultColumnList getColumnListInTempTable() {
814
815        return columnListInTempTable;
816    }
817
818    public void setPxGranule(TPxGranule pxGranule) {
819        this.pxGranule = pxGranule;
820    }
821
822    public TPxGranule getPxGranule() {
823
824        return pxGranule;
825    }
826
827    private TPxGranule pxGranule;
828    private TFlashback flashback;
829
830    public void setFlashback(TFlashback flashback) {
831        this.flashback = flashback;
832    }
833
834    public TFlashback getFlashback() {
835        return flashback;
836    }
837
838    /**
839     * The expression that picks the point in time a table is read at: {@code AS OF <expr>} (Oracle flashback,
840     * Teradata temporal tables) or {@code AT | BEFORE (TIMESTAMP | OFFSET => <expr>)} (Snowflake time travel).
841     *
842     * @return the expression, or null when the table is read as it is now
843     */
844    public TExpression getPointInTime() {
845        if ((flashback != null) && (flashback.getExpr() != null)) {
846            return flashback.getExpr();
847        }
848        if (timeTravelClause != null) {
849            return timeTravelClause.getTimestamp() != null ? timeTravelClause.getTimestamp() : timeTravelClause.getOffset();
850        }
851        return null;
852    }
853
854    private int parenthesisCount = 0;
855    private int parenthesisAfterAliasCount = 0;
856    private boolean tableKeyword = false;
857    private boolean onlyKeyword = false;
858    private boolean npathOnClauseTable = false;
859
860    /**
861     * Redshift array-unnesting positional index alias.
862     * <p>
863     * When a Redshift array column is unnested in the FROM clause with a
864     * positional index, e.g.
865     * <pre>FROM schema.tbl t, t.array_col AS element AT idx</pre>
866     * this table represents {@code t.array_col} with element alias
867     * {@code element} (the regular {@link #getAliasClause() aliasClause}) and
868     * {@code idx} captured here as the array index alias.
869     */
870    private TObjectName arrayIndexAlias;
871
872    public TObjectName getArrayIndexAlias() {
873        return arrayIndexAlias;
874    }
875
876    public void setArrayIndexAlias(TObjectName arrayIndexAlias) {
877        this.arrayIndexAlias = arrayIndexAlias;
878    }
879
880    /**
881     * Redshift SUPER object unpivoting source, e.g.
882     * <pre>FROM c.c_orders AS o, UNPIVOT o AS val AT attr</pre>
883     * When {@code true} this table represents the unpivoted SUPER expression:
884     * the regular alias is the value alias and {@link #getArrayIndexAlias()}
885     * (from {@code AT}) is the attribute-name alias.
886     */
887    private boolean superUnpivot = false;
888
889    public boolean isSuperUnpivot() {
890        return superUnpivot;
891    }
892
893    public void setSuperUnpivot(boolean superUnpivot) {
894        this.superUnpivot = superUnpivot;
895    }
896
897    /**
898     * Marks this table as coming from a Teradata NPath function's ON clause.
899     * This is used for data lineage to trace back to the source table inside NPath.
900     */
901    public void setNPathOnClauseTable(boolean npathOnClauseTable) {
902        this.npathOnClauseTable = npathOnClauseTable;
903    }
904
905    /**
906     * Returns true if this table comes from a Teradata NPath function's ON clause.
907     */
908    public boolean isNPathOnClauseTable() {
909        return npathOnClauseTable;
910    }
911
912    public void setTableKeyword(boolean tableKeyword) {
913        this.tableKeyword = tableKeyword;
914    }
915
916    public void setOnlyKeyword(boolean onlyKeyword) {
917        this.onlyKeyword = onlyKeyword;
918    }
919
920    public boolean isTableKeyword() {
921
922        return tableKeyword;
923    }
924
925    public boolean isOnlyKeyword() {
926        return onlyKeyword;
927    }
928
929    public void setParenthesisCount(int parenthesisCount) {
930        this.parenthesisCount = parenthesisCount;
931    }
932
933    public void setParenthesisAfterAliasCount(int parenthesisAfterAliasCount) {
934        this.parenthesisAfterAliasCount = parenthesisAfterAliasCount;
935    }
936
937    public int getParenthesisCount() {
938
939        return parenthesisCount;
940    }
941
942    public int getParenthesisAfterAliasCount() {
943        return parenthesisAfterAliasCount;
944    }
945
946    private TMergeSqlStatement outputMerge;
947
948    public void setOutputMerge(TMergeSqlStatement outputMerge) {
949        this.outputMerge = outputMerge;
950    }
951
952    public TMergeSqlStatement getOutputMerge() {
953
954        return outputMerge;
955    }
956
957    private TPivotedTable pivotedTable;
958
959    public void setPivotedTable(TPivotedTable pivotedTable) {
960        this.pivotedTable = pivotedTable;
961    }
962
963    public TPivotedTable getPivotedTable() {
964
965        return pivotedTable;
966    }
967
968    private ETableEffectType effectType;
969
970    public void setEffectType(ETableEffectType effectType) {
971        this.effectType = effectType;
972    }
973
974    public ETableEffectType getEffectType() {
975
976        return effectType;
977    }
978
979    private TTable linkTable;
980
981    public void setLinkTable(TTable linkTable) {
982        this.linkTable = linkTable;
983        isLinkTable = (this.linkTable != null);
984    }
985
986    public TTable getLinkTable() {
987
988        return linkTable;
989    }
990
991    public boolean isLinkTable() {
992        return isLinkTable;
993    }
994
995    /**
996     * @deprecated As of v1.8.4.6, set in {@link #setLinkTable}
997     */
998    public void setLinkTable(boolean isLinkTable) {
999
1000        this.isLinkTable = isLinkTable;
1001    }
1002
1003    private boolean isLinkTable;
1004
1005
1006    public String getAliasName(){
1007        if (getAliasClause() == null) return "";
1008        TObjectName aliasName = getAliasClause().getAliasName();
1009        return (aliasName == null) ? "" : aliasName.toString();
1010    }
1011
1012    public boolean equalByName(String pTableName){
1013        if (getAliasClause() != null){
1014            return SQLUtil.sameName(this.dbvendor, ESQLDataObjectType.dotTable, getAliasClause().getAliasName().toString(), pTableName);
1015        }else{
1016            return SQLUtil.compareIdentifier(this.dbvendor, ESQLDataObjectType.dotTable, getName(), pTableName);
1017        }
1018    }
1019
1020    private TPTNodeList<THiveKeyValueProperty> tableProperties;
1021
1022    public void setTableProperties(TPTNodeList<THiveKeyValueProperty> tableProperties) {
1023        this.tableProperties = tableProperties;
1024    }
1025
1026    /**
1027      *
1028      * @return hive table property list
1029      */
1030
1031    public TPTNodeList<THiveKeyValueProperty> getTableProperties() {
1032        return tableProperties;
1033    }
1034
1035    private ArrayList<TLateralView> lateralViewList = null;
1036
1037    public void setLateralViewList(ArrayList<TLateralView> lateralViewList) {
1038        this.lateralViewList = lateralViewList;
1039    }
1040
1041    /** For the table of a LATERAL VIEW ({@link ETableSource#lateralView}), the view it was made from. */
1042    private TLateralView lateralView;
1043
1044    /**
1045     * The LATERAL VIEW this table stands for when it is the relation a Hive-family {@code LATERAL VIEW} adds to its
1046     * SELECT ({@link TLateralView#createATable}); null for any other table.
1047     */
1048    public TLateralView getLateralView() {
1049        return lateralView;
1050    }
1051
1052    public void setLateralView(TLateralView lateralView) {
1053        this.lateralView = lateralView;
1054    }
1055
1056    public ArrayList<TLateralView> getLateralViewList() {
1057        return lateralViewList;
1058
1059    }
1060
1061    /**
1062     * The columns this FROM item's call generates, in order (a built-in table function, {@code UNNEST}, a Hive-family
1063     * {@code LATERAL VIEW} generator), by the rule resolver2 resolves their names with
1064     * ({@link gudusoft.gsqlparser.resolver2.namespace.TableFunctionLayout}); null when the rule does not settle the
1065     * call's columns. An element is null for a column the vendor leaves without a name a query can use.
1066     *
1067     * @since 4.2.17
1068     */
1069    public java.util.List<String> getLateralColumnNames() {
1070        gudusoft.gsqlparser.resolver2.namespace.TableFunctionLayout layout =
1071                gudusoft.gsqlparser.resolver2.namespace.TableFunctionLayout.of(this);
1072        return layout == null ? null : layout.getNames();
1073    }
1074
1075    /**
1076     * Per column of {@link #getLateralColumnNames()}, the indexes in {@link #getLateralArguments()} of the arguments
1077     * whose values make it ({@code "0,1"}), or the empty string for a column computed over the generated rows (an
1078     * ordinal); null when the arguments are not settled.
1079     *
1080     * @since 4.2.17
1081     */
1082    public java.util.List<String> getLateralColumnInputs() {
1083        gudusoft.gsqlparser.resolver2.namespace.TableFunctionLayout layout =
1084                gudusoft.gsqlparser.resolver2.namespace.TableFunctionLayout.of(this);
1085        return layout == null ? null : layout.getInputs();
1086    }
1087
1088    /**
1089     * The indexes in {@link #getLateralArguments()} of the arguments whose values decide the rows the call generates
1090     * ({@code "0,1"}; empty when none, as for an array literal), or null when the rule does not settle the rows. The
1091     * rows can be settled while the columns are not ({@link #getLateralColumnNames()} null).
1092     *
1093     * @since 4.2.17
1094     */
1095    public String getLateralRowInputs() {
1096        gudusoft.gsqlparser.resolver2.namespace.TableFunctionLayout layout =
1097                gudusoft.gsqlparser.resolver2.namespace.TableFunctionLayout.of(this);
1098        return layout == null ? null : layout.getRowInputs();
1099    }
1100
1101    /**
1102     * Whether {@link #getLateralColumnNames()} are all the call's columns (false for an array of unknown element type,
1103     * whose STRUCT or ROW fields would be columns of their own).
1104     *
1105     * @since 4.2.17
1106     */
1107    public boolean isLateralLayoutClosed() {
1108        gudusoft.gsqlparser.resolver2.namespace.TableFunctionLayout layout =
1109                gudusoft.gsqlparser.resolver2.namespace.TableFunctionLayout.of(this);
1110        return layout != null && layout.isClosed();
1111    }
1112
1113    /**
1114     * The arguments {@link #getLateralColumnInputs()} index: the call's argument expressions (a named argument's
1115     * value), or the arrays of an {@code UNNEST} (an expression, or the column name of its legacy form); null when the
1116     * call has no layout.
1117     *
1118     * @since 4.2.17
1119     */
1120    public java.util.List<TParseTreeNode> getLateralArguments() {
1121        gudusoft.gsqlparser.resolver2.namespace.TableFunctionLayout layout =
1122                gudusoft.gsqlparser.resolver2.namespace.TableFunctionLayout.of(this);
1123        return layout == null ? null : layout.getArguments();
1124    }
1125
1126    private java.util.List<String> declaredFunctionColumns;
1127
1128    /**
1129     * The columns of the user table function this FROM item calls, as the catalog declares them (a CREATE FUNCTION of
1130     * the input registered before the call, or catalog metadata): recorded by resolver2 when it resolves the call, so
1131     * that {@link #getLateralColumnNames()} and resolver2 read one layout. Null when none is known.
1132     *
1133     * @since 4.2.17
1134     */
1135    public java.util.List<String> getDeclaredFunctionColumns() {
1136        return declaredFunctionColumns;
1137    }
1138
1139    /**
1140     * Sets the declared columns of the user table function this FROM item calls (see
1141     * {@link #getDeclaredFunctionColumns()}).
1142     *
1143     * @since 4.2.17
1144     */
1145    public void setDeclaredFunctionColumns(java.util.List<String> columns) {
1146        this.declaredFunctionColumns = columns == null ? null
1147                : java.util.Collections.unmodifiableList(new java.util.ArrayList<String>(columns));
1148    }
1149
1150    private TTableSample tableSample;
1151
1152    public void setTableSample(TTableSample tableSample) {
1153        this.tableSample = tableSample;
1154    }
1155
1156    public TTableSample getTableSample() {
1157
1158        return tableSample;
1159    }
1160
1161    public void setPartitionExtensionClause(TPartitionExtensionClause partitionExtensionClause) {
1162        this.partitionExtensionClause = partitionExtensionClause;
1163    }
1164
1165    private TPartitionExtensionClause partitionExtensionClause;
1166
1167    public TPartitionExtensionClause getPartitionExtensionClause() {
1168        return partitionExtensionClause;
1169    }
1170
1171    public void setOuterClause(TInformixOuterClause outerClause) {
1172        this.outerClause = outerClause;
1173    }
1174
1175    public TInformixOuterClause getOuterClause() {
1176
1177        return outerClause;
1178    }
1179
1180    private TInformixOuterClause outerClause;
1181
1182    private TXmlTable xmlTable;
1183
1184    private TFromTableList fromTableList;
1185
1186    public void setFromTableList(TFromTableList fromTableList) {
1187        this.fromTableList = fromTableList;
1188    }
1189
1190    public TFromTableList getFromTableList() {
1191
1192        return fromTableList;
1193    }
1194
1195    public TXmlTable getXmlTable() {
1196        return xmlTable;
1197    }
1198
1199    public void setXmlTable(TXmlTable xmlTable) {
1200
1201        this.xmlTable = xmlTable;
1202    }
1203
1204    private TOpenQuery openquery = null;
1205
1206    public void setTableHintList(TPTNodeList<TTableHint> tableHintList) {
1207        this.tableHintList = tableHintList;
1208    }
1209
1210
1211    public TPTNodeList<TTableHint> getTableHintList() {
1212
1213        return tableHintList;
1214    }
1215
1216    private TPTNodeList<TTableHint> tableHintList;
1217
1218    public TOpenQuery getOpenquery() {
1219        return openquery;
1220    }
1221
1222    public TSelectSqlStatement getSubquery() {
1223        return subquery;
1224    }
1225
1226    public void setOpenquery(TOpenQuery openquery) {
1227
1228        this.openquery = openquery;
1229    }
1230
1231    public void setOpenDatasource(TOpenDatasource openDatasource) {
1232        this.openDatasource = openDatasource;
1233    }
1234
1235    /**
1236     * Valid when {@link #tableType} is ftt_opendatasource.
1237     * @return
1238     */
1239    public TOpenDatasource getOpenDatasource() {
1240
1241        return openDatasource;
1242    }
1243
1244    private TOpenDatasource openDatasource = null;
1245
1246    public void setOpenXML(TOpenXML openXML) {
1247        this.openXML = openXML;
1248    }
1249
1250    public TOpenXML getOpenXML() {
1251
1252        return openXML;
1253    }
1254
1255    private TOpenXML openXML = null;
1256
1257    public TOpenRowSet getOpenRowSet() {
1258        return openRowSet;
1259    }
1260
1261    public void setOpenRowSet(TOpenRowSet openRowSet) {
1262
1263        this.openRowSet = openRowSet;
1264    }
1265
1266    private TOpenRowSet openRowSet = null;
1267
1268    public TContainsTable getContainsTable() {
1269        return containsTable;
1270    }
1271
1272    public void setContainsTable(TContainsTable containsTable) {
1273        this.containsTable = containsTable;
1274    }
1275
1276    private TContainsTable containsTable = null;
1277
1278    public TFunctionCall getFuncCall() {
1279        return funcCall;
1280    }
1281
1282    public void setFuncCall(TFunctionCall funcCall) {
1283        this.funcCall = funcCall;
1284    }
1285
1286
1287    private TFunctionCall funcCall = null;
1288
1289    private TMultiTargetList rowList;
1290
1291    /**
1292     * @deprecated As of v2.3.6.9, please use {@link #getValueClause()} instead
1293     *
1294     * row constructor like this: '(' RW_VALUES MultiTargets ')'
1295     * @return     TMultiTargetList
1296     */
1297    public TMultiTargetList getRowList() {
1298        return rowList;
1299    }
1300
1301    public boolean isBaseTable(){
1302       boolean retval = (tableType == ETableSource.objectname);
1303       if (retval){
1304          if (isCTEName()){
1305              retval = false;
1306          }
1307       }
1308
1309       if (retval){
1310           if (getFullName().startsWith("@")) {
1311               retval = false;
1312           }
1313       }
1314        return retval;
1315    }
1316    
1317    public void setCteColomnReferences(TObjectNameList cteColomnReferences) {
1318        this.cteColomnReferences = cteColomnReferences;
1319    }
1320
1321    private TObjectNameList cteColomnReferences = null;
1322
1323    public TObjectNameList getCteColomnReferences() {
1324        return cteColomnReferences;
1325    }
1326
1327    public void setObjectNameReferences(TObjectNameList objectNameReferences) {
1328
1329        this.objectNameReferences = objectNameReferences;
1330    }
1331
1332   private TCTE CTE;
1333
1334    public boolean setCTE(TCTE pCTE) {
1335        if (pCTE == null){
1336            this.CTE = null;
1337            return true;
1338        }
1339
1340        if ((!pCTE.isRecursive()) && (pCTE.getSubquery() != null) && (TParseTreeNode.subNodeInNode(this,pCTE.getSubquery()))){
1341            // 如果这个table是属于一个CTE的子查询,那么不再设置CTE,因为该table不可能是该CTE的引用, 但前提条件是
1342            // 1. 该 subquery 非 combined query
1343            // 2. 如果该 subquery 是 combined query , 该 table 属于第一个subquery 中的 table
1344
1345            // 如果不是以上两种情况,那么该table就是该CTE的引用,需要返回 true
1346            // refer to: public void testSearchTable1()
1347
1348            if (!pCTE.getSubquery().isCombinedQuery()) return false;
1349
1350            if (TParseTreeNode.subNodeInNode(this,pCTE.getSubquery().getLeftStmt())){
1351                return false;
1352            }
1353
1354        }
1355
1356        this.CTE = pCTE;
1357        return true;
1358    }
1359
1360    public TCTE getCTE() {
1361
1362        return CTE;
1363    }
1364
1365    public boolean isCTEName() {
1366
1367        return isCTEName;
1368    }
1369
1370    public void setCTEName(boolean CTEName) {
1371        isCTEName = CTEName;
1372    }
1373
1374    private boolean isCTEName = false;
1375
1376    public TSelectSqlStatement subquery = null;
1377    private THiveFromQuery hiveFromQuery;
1378
1379    public void setHiveFromQuery(THiveFromQuery hiveFromQuery) {
1380        this.hiveFromQuery = hiveFromQuery;
1381    }
1382
1383    /**
1384     * @deprecated As of v2.1.0.0, please use {@link #getSubquery()}
1385     * @return
1386     */
1387    public THiveFromQuery getHiveFromQuery() {
1388
1389        return hiveFromQuery;
1390    }
1391
1392    public TExpression getTableExpr() {
1393        return tableExpr;
1394    }
1395
1396    public void setTableExpr(TExpression tableExpr) {
1397        this.tableExpr = tableExpr;
1398    }
1399
1400    private TExpression tableExpr;
1401
1402    public void setTableType(ETableSource tableType) {
1403        this.tableType = tableType;
1404    }
1405
1406    /**
1407     *
1408     * @return what's kind of type this table is.
1409     *<ul>
1410     * <li>{@link ETableSource#objectname}, in from clause, a simple table/view name, reference: {@link TTable#tableName}<li>
1411     * <li>{@link ETableSource#subquery}, is a subquery that retrieves rows from the database, also known as derived table. reference: {@link TTable#subquery}</li>
1412     * <li>{@link ETableSource#tableExpr},it's usually a table-valued expression., reference: {@link TTable#tableExpr}</li>
1413     * <li>{@link ETableSource#function}, it's usually a table-valued function., reference: {@link TTable#funcCall}</li>
1414     * <li>{@link ETableSource#rowList}, it's constructed rows, reference: {@link TTable#rowList}</li>
1415     * <li>{@link ETableSource#containsTable}, CONTAINSTABLE clause of sql server. reference: {@link TTable#containsTable}, type of {@link TContainsTable}</li>
1416     * <li>{@link ETableSource#freetextTable}, FREETEXTTABLE  clause of sql server. reference: {@link TTable#containsTable}, type of {@link TContainsTable}</li>
1417     * <li>{@link ETableSource#openrowset}, OPENROWSET clause of sql server. reference: {@link TTable#openRowSet}, type of {@link TOpenRowSet}</li>
1418     * <li>{@link ETableSource#openxml}, OPENXML clause of sql server. reference: {@link TTable#openXML}, type of {@link TOpenXML }</li>
1419     * <li>{@link ETableSource#opendatasource}, OPENDATASOURCE clause of sql server. reference: {@link TTable#openDatasource}, type of {@link TOpenDatasource}</li>
1420     * <li>{@link ETableSource#openquery}, OPENQUERY clause of sql server. reference: {@link TTable#openquery}, type of (@link TOpenQuery)</li>
1421     * </ul>
1422     */
1423    public ETableSource getTableType() {
1424
1425        return tableType;
1426    }
1427
1428    private ETableSource tableType;
1429    
1430    public void setTableName(TObjectName tableName) {
1431        this.tableName = tableName;
1432        tableType = ETableSource.objectname;
1433        this.tableName.splitNameInQuotedIdentifier();
1434        if (this.getStartToken() == null){
1435            this.setStartToken(tableName.getStartToken());
1436        }
1437
1438        if (this.getEndToken() == null){
1439            this.setEndToken(tableName.getEndToken());
1440        }
1441    }
1442
1443    public TObjectName getTableName() {
1444        TObjectName ret = tableName;
1445
1446        if (tableType == null) return ret;
1447
1448        switch (tableType){
1449            case tableExpr:
1450                if (tableExpr.getExpressionType() == EExpressionType.function_t){
1451                    ret = tableExpr.getFunctionCall().getFunctionName();
1452                }
1453                break;
1454            case function:
1455                ret = getFuncCall().getFunctionName();
1456                break;
1457            case openrowset:
1458                ret = TObjectName.createObjectName (this.dbvendor, EDbObjectType.table,new TSourceToken("openrowset"));
1459                break;
1460            case rowList:
1461                ret = TObjectName.createObjectName (this.dbvendor, EDbObjectType.table,new TSourceToken("(values_table)"));
1462                break;
1463            case subquery:
1464                ret = TObjectName.createObjectName (this.dbvendor,EDbObjectType.table,new TSourceToken("subquery"));
1465                break;
1466            case td_unpivot:
1467                ret = TObjectName.createObjectName (this.dbvendor,EDbObjectType.table,new TSourceToken("td_unpivot"));
1468                break;
1469            case pivoted_table:
1470                // Check if this is an UNPIVOT to use the correct suffix
1471                String pivotSuffix = "(piviot_table)";
1472                if (getPivotedTable() != null && getPivotedTable().getPivotClause() != null
1473                    && getPivotedTable().getPivotClause().getType() == TPivotClause.unpivot) {
1474                    pivotSuffix = "(unpivot_table)";
1475                }
1476                ret = TObjectName.createObjectName (this.dbvendor,EDbObjectType.table,new TSourceToken(tableName+pivotSuffix));
1477                break;
1478            case xmltable:
1479                ret = TObjectName.createObjectName (this.dbvendor,EDbObjectType.table,new TSourceToken("xmltable"));
1480                break;
1481            case jsonTable:
1482                ret = getJsonTable().getFunctionName();// TObjectName.createObjectName (this.dbvendor,EDbObjectType.table,new TSourceToken("jsontable"));//
1483                break;
1484            case containsTable:
1485                if (containsTable.getType() == TContainsTable.containstable){
1486                    ret = TObjectName.createObjectName (this.dbvendor,EDbObjectType.table,new TSourceToken("containsTable"));
1487                }else {
1488                    ret = TObjectName.createObjectName (this.dbvendor,EDbObjectType.table,new TSourceToken("freetexttable"));
1489                }
1490                break;
1491            case opendatasource:
1492                ret = TObjectName.createObjectName (this.dbvendor,EDbObjectType.table,new TSourceToken("opendatasource"));
1493                break;
1494            case unnest:
1495                ret = TObjectName.createObjectName (this.dbvendor,EDbObjectType.table,new TSourceToken(getAliasName()+"(unnest table)"));
1496                break;
1497            case openxml:
1498                ret = TObjectName.createObjectName (this.dbvendor,EDbObjectType.table,new TSourceToken("openxml"));
1499                break;
1500            case openquery:
1501                ret = TObjectName.createObjectName (this.dbvendor,EDbObjectType.table,new TSourceToken("openquery"));
1502                break;
1503            case output_merge:
1504                ret = TObjectName.createObjectName (this.dbvendor,EDbObjectType.table,new TSourceToken("merge"));
1505                break;
1506            case externalTable:
1507                ret = this.tableName;
1508                break;
1509            case join:
1510                ret = TObjectName.createObjectName (this.dbvendor,EDbObjectType.table,new TSourceToken("join_expr"));
1511                break;
1512        }
1513        if (ret == null) {
1514            return TObjectName.createObjectName (this.dbvendor,EDbObjectType.table,new TSourceToken("unknown"));
1515        }
1516        else  return ret;
1517    }
1518
1519    private TObjectName tableName;
1520
1521    public TTable(TObjectName pobjectname){
1522           this.tableName = pobjectname;
1523           this.setStartToken(this.tableName.getStartToken());
1524           this.setEndToken(this.tableName.getEndToken());
1525    }
1526
1527
1528    public TTable(){
1529           this.tableName = null;
1530    }
1531
1532    public String getName() {
1533        if (tableName != null)
1534            return tableName.getObjectString();
1535        else
1536            return getTableName().toString();
1537    }
1538
1539
1540    public String getPrefixServer(){
1541       if (tableName != null) return tableName.getServerString();
1542       else return "";
1543    }
1544    public  String getPrefixDatabase(){
1545        if (tableName != null) return tableName.getDatabaseString();
1546        else return "";
1547    }
1548    public String getPrefixSchema(){
1549        if (tableName != null) return tableName.getSchemaString();
1550        else return "";
1551    }
1552
1553    public boolean isIncludeColumnAlias(){
1554       //  return (this.getAliasClause() != null);
1555        return false;
1556        // ToDo
1557    }
1558
1559
1560    public int searchColumnInAlias(TObjectName pColumn){
1561        if (getAliasClause() == null) return -1;
1562        return getAliasClause().searchColumn(pColumn);
1563    }
1564
1565    public boolean checkTableByName(String pTablename){
1566        boolean lcResult = false;
1567        String a,b,c;
1568        b = TBaseType.getTextWithoutQuoted(pTablename);
1569        if (this.getAliasClause() != null){
1570            a = TBaseType.getTextWithoutQuoted(this.getAliasName());
1571            lcResult = a.equalsIgnoreCase(b);
1572        }
1573        if (lcResult) return true;
1574        c = TBaseType.getTextWithoutQuoted(this.getTableName().toString());
1575        lcResult = c.equalsIgnoreCase(b);
1576        return lcResult;
1577    }
1578
1579    public boolean searchColumn(TSelectSqlStatement select, String tableName, TObjectName pColumn, boolean pMustIn){
1580        // pColumn maybe qualified, so check the table name at first
1581        boolean lcResult = false, isSameTable = false;
1582        if ( tableName.length() == 0){
1583            isSameTable = true;
1584        }else {
1585            isSameTable = checkTableByName(tableName);
1586        }
1587
1588        if (!isSameTable) return lcResult;
1589
1590        if (this.isBaseTable()){
1591            lcResult = select.fireOnMetaDatabaseTableColumn(this.getPrefixServer(),this.getPrefixDatabase(),this.getPrefixSchema(),this.getName(),pColumn.getColumnNameOnly());
1592        }else{
1593            if (this.getTableType() == ETableSource.subquery){
1594                if (this.isIncludeColumnAlias()){
1595                    lcResult = this.searchColumnInAlias(pColumn)>=0;
1596                }else{
1597                    lcResult = this.getSubquery().searchColumnInResultSet(pColumn,pMustIn);
1598                }
1599            }else if (this.isCTEName()){
1600                lcResult = this.getCTE().searchColumnInResultSet(select,this,pColumn,pMustIn);
1601            }
1602        }
1603
1604        if (lcResult){
1605            this.getLinkedColumns().addObjectName(pColumn);
1606            pColumn.setSourceTable(this);
1607        }else if (pMustIn){
1608            this.getLinkedColumns().addObjectName(pColumn);
1609            pColumn.setSourceTable(this);
1610            lcResult = true;
1611        }
1612
1613        if (!lcResult){
1614            if (this.getTableType() == ETableSource.pivoted_table){
1615                if (this.getLinkedColumns().size() > 0){
1616                    for(TObjectName c: this.getLinkedColumns()){
1617                        lcResult = c.toString().equalsIgnoreCase(pColumn.getColumnNameOnly());
1618                        if (lcResult) break;
1619                    }
1620                }
1621            }
1622        }
1623
1624        return lcResult;
1625    }
1626
1627    private TObjectNameList linkedColumns = null;
1628
1629    /**
1630     * @deprecated , this method is deprecated since version 3.3.1.0, don't use it in TSQLResolver2
1631     * @return
1632     */
1633    public TObjectNameList getLinkedColumns() {
1634        if (linkedColumns == null) {
1635            linkedColumns = new TObjectNameList();
1636            linkedColumns.setObjectType(TObjectName.ttobjColumn);
1637        }
1638        return linkedColumns;
1639    }
1640
1641
1642
1643    public String getFullNameWithAliasString() {
1644        if (getAliasClause() != null){
1645            return getFullName()+" "+getAliasClause().toString();
1646        }else{
1647            return getFullName();
1648        }
1649    }
1650
1651    public String getFullName() {
1652        if (tableName == null) return  null;
1653        return tableName.toString();
1654        /*
1655        if (tableName.getSchemaString() == null)
1656        {return this.getName();}
1657        else{
1658            return tableName.getSchemaString()+"."+this.getName();
1659        }
1660          */
1661
1662    }
1663
1664    /**
1665     *
1666     * @return column name related to this table.
1667     * @deprecated As of v1.6.0.1, use  {@link #getLinkedColumns()} instead
1668     */
1669    public TObjectNameList getObjectNameReferences() {
1670        if (objectNameReferences == null){
1671            objectNameReferences = new TObjectNameList();
1672            objectNameReferences.setObjectType(TObjectName.ttobjColumn);
1673        }
1674        return objectNameReferences;
1675    }
1676
1677    private TPivotClause pivotClause = null;
1678
1679    public void setPivotClause(TPivotClause pivotClause) {
1680        this.pivotClause = pivotClause;
1681    }
1682
1683    /**
1684     * @deprecated As of v1.6.2.4, replaced by {@link gudusoft.gsqlparser.nodes.TPivotedTable}
1685     * @return
1686     */
1687    public TPivotClause getPivotClause() {
1688
1689        return pivotClause;
1690    }
1691
1692    /**
1693     * Stored all column names that belong to this table.
1694     */
1695    private TObjectNameList objectNameReferences = null;
1696
1697    public TTableReferenceList tablerefs = new TTableReferenceList();
1698
1699    private TDataChangeTable datachangeTable = null;
1700
1701    public void setDatachangeTable(TDataChangeTable datachangeTable) {
1702        this.datachangeTable = datachangeTable;
1703    }
1704
1705    /**
1706     * DB2 data change
1707     * @return
1708     */
1709    public TDataChangeTable getDatachangeTable() {
1710        return datachangeTable;
1711    }
1712
1713
1714    public boolean isTableRefBelongToThisTable(TTableReference tableref){
1715
1716        boolean retval = (this.tableName == tableref.objectname);
1717        if (retval) {return retval;}
1718        if (SQLUtil.sameName(this.dbvendor, ESQLDataObjectType.dotTable, this.tableName.getObjectString(), tableref.objectname.getObjectString())){
1719            if ((this.tableName.getSchemaString() == null)
1720                ||(tableref.objectname.getSchemaString() == null)
1721                    ) {
1722                retval = true;
1723            }else{
1724                retval = SQLUtil.sameName(this.dbvendor, ESQLDataObjectType.dotSchema, this.tableName.getSchemaString(), tableref.objectname.getSchemaString());
1725            }
1726        }
1727        return retval;
1728    }
1729
1730    public void accept(TParseTreeVisitor v){
1731        v.preVisit(this);
1732
1733        v.postVisit(this);
1734    }
1735
1736    public void setColumnDefinitions(TColumnDefinitionList columnDefinitions) {
1737        this.columnDefinitions = columnDefinitions;
1738    }
1739
1740    public void acceptChildren(TParseTreeVisitor v){
1741        v.preVisit(this);
1742
1743        switch(getTableType()){
1744            case objectname:{
1745                getTableName().acceptChildren(v);
1746                if (getFlashback() != null){
1747                    getFlashback().acceptChildren(v);
1748                }
1749                if (getTimeTravelClause() != null){
1750                    getTimeTravelClause().acceptChildren(v);
1751                }
1752                break;
1753            }
1754            case tableExpr:{
1755                getTableExpr().acceptChildren(v);
1756                break;
1757            }
1758            case subquery:{
1759                getSubquery().acceptChildren(v);
1760                break;
1761            }
1762            case function:{
1763                getFuncCall().acceptChildren(v);
1764                break;
1765            }
1766            case pivoted_table:{
1767                if (this.getEffectType() == ETableEffectType.tetPivot) break;
1768                getPivotedTable().acceptChildren(v);
1769                break;
1770            }
1771            case output_merge:{
1772                if (getOutputMerge() != null) getOutputMerge().acceptChildren(v);
1773                //e_table_reference.setTextContent(node.getOutputMerge().toString());
1774                break;
1775            }
1776            case containsTable:{
1777                getContainsTable().acceptChildren(v);
1778                break;
1779            }
1780
1781            case openrowset:{
1782                getOpenRowSet().acceptChildren(v);
1783                break;
1784            }
1785
1786            case openxml:{
1787                getOpenXML().acceptChildren(v);
1788                break;
1789            }
1790
1791            case opendatasource:{
1792                getOpenDatasource().acceptChildren(v);
1793                break;
1794            }
1795
1796            case openquery:{
1797                getOpenquery().acceptChildren(v);
1798                break;
1799            }
1800
1801            case datachangeTable:{
1802                getDatachangeTable().acceptChildren(v);
1803                break;
1804            }
1805            case rowList:{
1806                getValueClause().acceptChildren(v);
1807                break;
1808            }
1809            case xmltable:{
1810                getXmlTable().acceptChildren(v);
1811                break;
1812            }
1813            case jsonTable:{
1814                getJsonTable().acceptChildren(v);
1815                break;
1816            }
1817            case informixOuter:{
1818                getOuterClause().acceptChildren(v);
1819                break;
1820            }
1821
1822            case table_ref_list:{
1823                getFromTableList().acceptChildren(v);
1824                break;
1825            }
1826            case hiveFromQuery:{
1827                getHiveFromQuery().acceptChildren(v);
1828                break;
1829            }
1830            case externalTable:
1831                this.columnDefinitions.acceptChildren(v);
1832                break;
1833            case join:
1834                this.joinExpr.acceptChildren(v);
1835                break;
1836            case unnest:
1837                this.getUnnestClause().acceptChildren(v);
1838                break;
1839            default:
1840                //sb.append(node.toString().replace(">","&#62;").replace("<","&#60;"));
1841                break;
1842
1843        }
1844
1845
1846        if (tableOperatorPartitionBy != null) tableOperatorPartitionBy.acceptChildren(v);
1847        if (tableOperatorOrderBy != null) tableOperatorOrderBy.acceptChildren(v);
1848
1849        if (getPxGranule() != null){
1850            getPxGranule().acceptChildren(v);
1851        }
1852
1853        if (getTableSample() != null){
1854           getTableSample().acceptChildren(v);
1855        }
1856
1857        if (getAliasClause() != null){
1858            getAliasClause().acceptChildren(v);
1859        }
1860
1861
1862        if (getTableHintList() != null){
1863            for(int i=0;i<getTableHintList().size();i++){
1864                TTableHint tableHint = getTableHintList().getElement(i);
1865                tableHint.acceptChildren(v);
1866            }
1867        }
1868
1869        if (getLateralViewList() != null){
1870            for(TLateralView lateralView:getLateralViewList()){
1871                lateralView.acceptChildren(v);
1872            }
1873        }
1874
1875
1876        v.postVisit(this);
1877    }
1878
1879    public void setRowList(TMultiTargetList rowList) {
1880        this.rowList = rowList;
1881    }
1882
1883    public void setSubquery(TSelectSqlStatement subquery) {
1884        this.subquery = subquery;
1885    }
1886
1887    public void setLinkedColumns(TObjectNameList linkedColumns) {
1888        this.linkedColumns = linkedColumns;
1889    }
1890
1891    public void setTablerefs(TTableReferenceList tablerefs) {
1892        this.tablerefs = tablerefs;
1893    }
1894
1895    private boolean starColumnExpanded = false;
1896
1897    private ArrayList<String> expandedStarColumns = new ArrayList<String>();
1898
1899    private void getExpandStarColumnsFromSubquery(TSelectSqlStatement subquery, ArrayList<String> expandedStarColumns){
1900
1901        for(TResultColumn resultColumn:subquery.getResultColumnList()){
1902            if (resultColumn.toString().endsWith("*")){
1903                TTable srcTable  = resultColumn.getExpr().getObjectOperand().getSourceTable();
1904                if ( srcTable!= null){
1905                    ArrayList<String> list = srcTable.getExpandedStarColumns();
1906                    expandedStarColumns.addAll(list);
1907                }
1908            }else{
1909                expandedStarColumns.add(resultColumn.getDisplayName());
1910            }
1911        }
1912    }
1913
1914    /**
1915     * If a star column is linked to this table, use this method to returns all underlying columns that represented by
1916     * the star column.
1917     * <br>
1918     * <br> The returned column always prefixed with
1919     * <br> 1. the table alias.
1920     * <br> 2. the table name
1921     * <br> 3. the alias of query (if the table is a subquery)
1922     * <br> 4. empty if the no alias is specified for a subquery
1923     *
1924     * @return a list of columns
1925     */
1926    public ArrayList<String> getExpandedStarColumns(){
1927        if (starColumnExpanded) return expandedStarColumns;
1928
1929        switch (getTableType()){
1930            case objectname:
1931                if (this.isCTEName()){
1932                    if (this.getCteColomnReferences()!=null){
1933                        for(TObjectName n:this.getCteColomnReferences()){
1934                            expandedStarColumns.add(n.toString());
1935                        }
1936                    }else if (this.getCTE().getSubquery() != null){
1937                        this.getExpandStarColumnsFromSubquery(this.getCTE().getSubquery(),this.expandedStarColumns);
1938                    }
1939                }else{
1940                    if (this.getSqlEnv() != null){
1941                        ArrayList<String> columns;
1942                        if (tableName != null && (tableName.getServerToken() != null || tableName.getDblink() != null)) {
1943                            // The composed name drops the server or the link; ask with the whole name, so a linked
1944                            // server or a database link never meets a table declared without a database (Mantis 4751).
1945                            TSQLTable table = this.getSqlEnv().searchTable(tableName);
1946                            columns = table == null ? null : table.getColumns(false);
1947                        } else {
1948                            columns = this.getSqlEnv().getColumnsInTable(this.getPrefixDatabase() +"."+this.getPrefixSchema()+"."+this.getName(),false);
1949                        }
1950                        if (columns != null) expandedStarColumns = columns;
1951                        if (this.getAliasClause() != null){
1952                            //replace table name with alias
1953                        }
1954                    }
1955                    if ((expandedStarColumns.size() == 0)&&(getLinkedColumns().size()>0)){
1956                        for(TObjectName linkColumn:getLinkedColumns()){
1957                            if (linkColumn.toString().endsWith("*")) continue;
1958                            String s = linkColumn.getColumnNameOnly();
1959                            if (this.getAliasClause() != null){
1960                                s = this.getAliasName()+"."+s;
1961                            }else{
1962                                s = this.getName()+"."+s;
1963                            }
1964                            if (expandedStarColumns.indexOf(s) == -1) expandedStarColumns.add(s);
1965                        }
1966                    }
1967                }
1968
1969                break;
1970            case subquery:
1971                this.getExpandStarColumnsFromSubquery(this.getSubquery(),this.expandedStarColumns);
1972                break;
1973            default:
1974                break;
1975        }
1976
1977        starColumnExpanded = true;
1978        return expandedStarColumns;
1979    }
1980
1981//    @Override
1982//    public String toString(){
1983//        if (getAliasClause() != null) return getAliasClause().toString();
1984//
1985//        switch (getTableType()){
1986//            case objectname:
1987//                return getName().toString();
1988//                break;
1989//            default:
1990//
1991//                break;
1992//        }
1993//    }
1994
1995    @Override
1996    public String toString(){
1997        String ret = super.toString();
1998        if (ret != null) return  ret;
1999        if (tableName != null) return tableName.getObjectString();
2000        return null;
2001    }
2002
2003}