001package gudusoft.gsqlparser.resolver;
002
003import gudusoft.gsqlparser.*;
004import gudusoft.gsqlparser.compiler.TContext;
005import gudusoft.gsqlparser.compiler.TFunction;
006import gudusoft.gsqlparser.compiler.TScope;
007import gudusoft.gsqlparser.compiler.TStmtScope;
008import gudusoft.gsqlparser.nodes.*;
009import gudusoft.gsqlparser.sqlenv.TSQLColumn;
010import gudusoft.gsqlparser.sqlenv.TSQLTable;
011import gudusoft.gsqlparser.stmt.*;
012
013import java.util.ArrayList;
014import java.util.Stack;
015
016/**
017 * TRelationResolver 负责为SQL语句中的每个表收集和初始化属性(attributes)。
018 * 这些属性主要包含表的列信息,用于后续解析器(如TAttributeResolver)建立表和列之间的关系。
019 * 
020 * 工作流程:
021 * 1. 遍历SQL语句中的所有表
022 * 2. 根据表的类型(普通表、函数表、子查询等)收集属性
023 * 3. 将属性存储在TTable.attributes中
024 * 
025 * 属性来源:
026 * - 普通表:从数据库元数据(TSQLTable)获取列信息
027 * - 函数表:从函数定义获取返回列
028 * - 子查询:由TAttributeResolver处理
029 * - XML表:从XML表定义获取列
030 * - 表达式:生成通配符属性
031 */
032public class TRelationResolver extends TParseTreeVisitor {
033    private final TContext globalScope;        // 全局上下文,包含SQL环境
034    private final TStatementList sqlStatements; // 要分析的SQL语句列表
035    private final Stack<TScope> scopeStack;    // 作用域栈,用于维护嵌套关系
036    private final TSQLResolver resolver;
037
038    /**
039     * 创建关系解析器实例。
040     * @param sqls 要分析的SQL语句列表
041     * @param scope 包含SQL环境的全局上下文
042     */
043    public TRelationResolver(TStatementList sqls, TContext scope, TSQLResolver resolver) {
044        this.globalScope = scope;
045        this.sqlStatements = sqls;
046        this.scopeStack = new Stack<>();
047        this.resolver = resolver;
048        scopeStack.push(globalScope);
049    }
050
051    /**
052     * 解析所有SQL语句中的表关系。
053     * 遍历语句树,为每个表节点收集属性。
054     */
055    public void resolve() {
056        //System.out.println("==== Start resolving relations:");
057
058        for (int i = 0; i < sqlStatements.size(); i++) {
059           // System.out.println("\n==== Start SQL: "+ (i+1));
060            sqlStatements.get(i).acceptChildren(this);
061        }
062    }
063
064    /**
065     * 处理struct_t类型的表函数。
066     * 初始化函数返回的属性列表。
067     */
068    @Override
069    public void preVisit(TTableFunction node) {
070        if (node.getFunctionType() == EFunctionType.struct_t) {
071            //System.out.println("Found struct "+node.toString());
072            int originalSize = node.getAttributes().size();
073            node.initAttributes();  // 初始化struct_t函数的返回列作为属性
074            resolver.reportNewColumns(node.getAttributes().size() - originalSize);
075        }
076    }
077
078    /**
079     * 处理表节点,根据表类型收集相应的属性。
080     * 这是属性收集的核心方法。
081     */
082    @Override
083    public void preVisit(TTable node) {
084        switch (node.getTableType()) {
085            case objectname:
086                handleObjectNameTable(node);  // 处理普通表
087                break;
088            case function:
089                handleFunctionTable(node);    // 处理函数表
090                break;
091            case subquery:
092                //  需要从 sub-query 的 select list 中获取 attributeNode,
093                //  当 select list 中有 star column时,需要先在 attributeResolver 中处理完成后,
094                //  才有可用的数据提供给这个 table, 因此,本部分的展开放在 attributeResolver 的 postVisit(TTable node) 完成。
095//                for(int i=0;i<node.getLinkedColumns().size();i++){
096//                    TObjectName column = node.getLinkedColumns().getObjectName(i);
097//                    System.out.println("Linked column from Subquery: "+node.getAliasName()+": "+column.toString());
098//                }
099                break;
100            case xmltable:
101                handleXMLTable(node);         // 处理XML表
102                break;
103            case tableExpr:
104                handleTableExpression(node);  // 处理表达式
105                break;
106            case rowList:
107                int originalSize = node.getAttributes().size();
108                node.initAttributeForRowList(); // 处理行列表
109                resolver.reportNewColumns(node.getAttributes().size() - originalSize);
110                break;
111        }
112    }
113
114    public void postVisit(TTable table) {
115        int originalSize = 0;
116        switch (table.getTableType()) {
117            case join:
118                originalSize = table.getAttributes().size();
119                table.initAttributesForJoin();
120                resolver.reportNewColumns(table.getAttributes().size() - originalSize);
121                break;
122            case unnest:
123                TStmtScope current = (TStmtScope)scopeStack.peek();
124                TSelectSqlStatement select = (TSelectSqlStatement)(current.getStmt());
125                originalSize = table.getAttributes().size();
126                table.initAttributesForUnnest(globalScope.getSqlEnv(), select);
127                resolver.reportNewColumns(table.getAttributes().size() - originalSize);
128                break;
129            case subquery:
130                // 完成 sub-query 类型的 table 的 attributeNode 的收集,以供 attribute resolve 时使用。
131                String prefix = "";
132                if (table.getAliasClause() != null) {
133                    prefix = table.getAliasClause().toString() + ".";
134                }
135
136                originalSize = table.getAttributes().size();
137                table.initAttributesFromSubquery(table.getSubquery(), prefix);
138                resolver.reportNewColumns(table.getAttributes().size() - originalSize);
139
140                if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
141                     TBaseType.log(String.format("Prepare attributes for subquery: %s", table.getDisplayName()), TLog.DEBUG, table.getStartToken());
142                }
143                for (TAttributeNode a : table.getAttributes()) {
144                    if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
145                        TBaseType.log(String.format("\tAttriubte: %s", a.getName()), TLog.DEBUG, table.getStartToken());
146                    }
147                }
148                break;
149            case objectname:
150                if (table.isCTEName()) {
151                    // 这是一个指向 CTE 的 table ref,根据 CTE 生成该 table ref自己的 attribute
152                    originalSize = table.getAttributes().size();
153                    table.initAttributesFromCTE(table.getCTE());
154                    resolver.reportNewColumns(table.getAttributes().size() - originalSize);
155                }
156                break;
157            case pivoted_table:
158                originalSize = table.getAttributes().size();
159                table.initAttributesForPivotTable();
160                resolver.reportNewColumns(table.getAttributes().size() - originalSize);
161                if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
162                    TBaseType.log(String.format("Prepare attributes for pivot table: %s", table.getDisplayName()), TLog.DEBUG, table.getStartToken());
163                }
164                for (TAttributeNode a : table.getAttributes()) {
165                    if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
166                        TBaseType.log(String.format("\tAttribute: %s", a.getName()), TLog.DEBUG, table.getStartToken());
167                    }
168                }
169                break;
170        }
171
172//        if (table.getLateralViewList() != null){
173//            originalSize = table.getAttributes().size();
174//            for (int i = 0; i < table.getLateralViewList().size(); i++) {
175//                TLateralView lateralView = table.getLateralViewList().get(i);
176//                if (lateralView.getTableFunction() != null) {
177//                    TAttributeNode.addNodeToList(new TAttributeNode(lateralView.getTableFunction().getDisplayName() + ".*", table), table.getAttributes());
178//                }
179//            }
180//            resolver.reportNewColumns(table.getAttributes().size() - originalSize);
181//        }
182    }
183
184
185    /**
186     * 处理普通表,从数据库元数据获取列信息作为属性。
187     */
188    private void handleObjectNameTable(TTable node) {
189        if (node.isCTEName()) return;  // CTE由TAttributeResolver处理
190        
191
192        int originalSize = node.getAttributes().size();
193
194        TSQLTable sqlTable = globalScope.getSqlEnv().searchTable(node.getTableName());
195        if (sqlTable == null) {
196            // 如果该表有已经关联的列,也添加为属性,提高后续匹配的准确性
197            if (node.getLinkedColumns().size() > 0) {
198                for (TObjectName column : node.getLinkedColumns()) {
199                    if (column.getCandidateTables().size() > 1) continue;
200
201                    TAttributeNode.addNodeToList(new TAttributeNode(
202                        node.getDisplayName() + "." + column.getColumnNameOnly(),
203                        node
204                    ), node.getAttributes());
205
206                    //System.out.println("Table-column relation added: "+node.getDisplayName()+"."+column.getColumnNameOnly());
207                }
208            }
209            // 表未找到时添加通配符属性,放在实际的列属性之后,降低通配符属性的优先级
210            TAttributeNode.addNodeToList(new TAttributeNode(node.getDisplayName() + ".*", node), node.getAttributes());
211
212           // System.out.println("Table: "+node.toString()+" is NOT found in SQLEnv.");
213        } else {
214            // 表找到时添加具体列属性
215            node.setResolvedTable(sqlTable);
216            for (TSQLColumn column : sqlTable.getColumnList()) {
217
218                TAttributeNode.addNodeToList(new TAttributeNode(
219                    node.getDisplayName() + "." + column.getName(),
220                    node,
221                    column
222                ), node.getAttributes());
223            }
224        }
225
226        resolver.reportNewColumns(node.getAttributes().size() - originalSize);
227
228        if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
229            TBaseType.log(String.format("Prepare attributes ( num= %d ) for table: %s",node.getAttributes().size(),node.getDisplayName()), TLog.DEBUG,node.getStartToken());
230        }
231        int c = 0;
232        for(TAttributeNode a:node.getAttributes()){
233            if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
234                TBaseType.log(String.format("\tAttribute: %s",a.getName()),TLog.DEBUG,node.getStartToken());
235            }
236            c++;
237            if (c > TLog.OUTPUT_ATTRIBUTES_MAX) {
238                if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){    
239                    TBaseType.log(String.format("\t...skipped after output %d attributes",c-1),TLog.DEBUG,node.getStartToken());
240                }
241                break;
242            }
243        }
244    }
245
246    /**
247     * 处理表函数,初始化函数返回的列作为属性。
248     */
249    private void handleFunctionTable(TTable node) {
250        int originalSize = node.getAttributes().size();
251        node.initAttributeForTableFunction();
252        if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
253            TBaseType.log(String.format("Prepare attributes for table function: %s",node.getDisplayName()), TLog.DEBUG,node.getStartToken());
254        }
255        for(TAttributeNode a:node.getAttributes()){
256            if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
257                TBaseType.log(String.format("\tAttriubte: %s",a.getName()),TLog.DEBUG,node.getStartToken());
258            }
259        }
260        resolver.reportNewColumns(node.getAttributes().size() - originalSize);
261    }
262
263    /**
264     * 处理XML表,初始化XML表定义的列作为属性。
265     */
266    private void handleXMLTable(TTable node) {
267        int originalSize = node.getAttributes().size();
268        node.initAttributeForXMLTable();
269        if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
270            TBaseType.log(String.format("Prepare attributes for XML Table : %s",node.getDisplayName()), TLog.DEBUG,node.getStartToken());
271        }
272        for(TAttributeNode a:node.getAttributes()){
273            if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
274                TBaseType.log(String.format("\tAttriubte: %s",a.getName()),TLog.DEBUG,node.getStartToken());
275            }
276        }
277        resolver.reportNewColumns(node.getAttributes().size() - originalSize);
278    }
279
280    /**
281     * 处理表达式,添加通配符属性。
282     */
283    private void handleTableExpression(TTable node) {
284        //node.getAttributes().add(new TAttributeNode(node.getDisplayName() + ".*", node));
285        int originalSize = node.getAttributes().size();
286        TAttributeNode.addNodeToList(new TAttributeNode(node.getDisplayName() + ".*", node),node.getAttributes());
287        if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
288            TBaseType.log(String.format("Prepare attributes for table expr function: %s",node.getDisplayName()), TLog.DEBUG,node.getStartToken());
289        }
290        for(TAttributeNode a:node.getAttributes()){
291            if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
292                TBaseType.log(String.format("\tAttribute: %s",a.getName()),TLog.DEBUG,node.getStartToken());
293            }
294        }
295        resolver.reportNewColumns(node.getAttributes().size() - originalSize);
296    }
297
298    public void preVisit(TJoinExpr joinExpr){
299        switch (joinExpr.getJointype()){
300            case crossapply:
301                //SELECT
302                //     [FacilityAccountID] = CAST(enc.FacilityAccountID AS VARCHAR(255))
303                //    ,[RowSourceDSC] = CAST('Cerner' AS VARCHAR(255))
304                //    ,[AttendingProviderID] = CAST(CAST(p.PersonnelID AS NUMERIC(38,0)) AS VARCHAR(255))
305                //    ,[EDWLastModifiedDTS] = CAST(p.EDWLastModifiedDTS AS DATETIME2(7))
306                //FROM Shared.Clinical.EncounterLink AS enc
307                //CROSS APPLY
308                //(
309                //    SELECT TOP 1
310                //        a.PersonnelID
311                //       ,a.EDWLastModifiedDTS
312                //    FROM Cerner.Encounter.EncounterToPersonnelRelationship AS a
313                //    WHERE a.EncounterID = enc.SourcePatientEncounterID
314                //      AND a.ActiveStatusCVCD = 506
315                //      AND a.EncounterPersonnelRelationshipCVCD = 1206
316                //      AND a.ContributorSystemCVCD = 16048
317                //    ORDER BY
318                //        a.UpdateDTS DESC
319                //) AS p
320
321                //  cross apply 后的 subquery 的 enclose scope 为父层的 select 语句,
322                // 不同于其他出现在 from clause的 subquery, 他们的 enclose scope 不能为父层的 select 语句
323                if (joinExpr.getRightTable().getTableType() == ETableSource.subquery){
324                    TSelectSqlStatement subquery = joinExpr.getRightTable().getSubquery();
325                    subquery.setSubQueryInFromClauseCanUseParentSelectAsEnclosingScope(true);
326                }
327                break;
328        }
329    }
330
331    public void preVisit(TPivotClause pivotClause){
332        int originalSize = pivotClause.size();
333        pivotClause.initAttributes();
334        resolver.reportNewColumns(pivotClause.size() - originalSize);
335    }
336
337
338
339    public void preVisit(TMergeInsertClause node){
340
341        TStmtScope current = (TStmtScope)scopeStack.peek();
342
343        TMergeSqlStatement mergeSqlStatement  = (TMergeSqlStatement)(current.getStmt());
344
345        TTable table = mergeSqlStatement.getUsingTable();
346        TResultColumnList valuelist = node.getValuelist();
347
348        if (valuelist != null){
349            for(int i=0;i<valuelist.size();i++){
350                TResultColumn resultColumn = valuelist.getResultColumn(i);
351                if (resultColumn.getExpr() != null){
352                    TExpression expression = resultColumn.getExpr();
353                    ArrayList<TObjectName> columns = expression.getColumnsInsideExpression();
354                    for(TObjectName column:columns){
355                        //System.out.println("add columns to table:"+table.getDisplayName()+":\t"+column.toString());
356                        // table.getLinkedColumns().addObjectName(column);
357                        TAttributeNode attributeNode ;
358
359                        if ((table.getTableType() == ETableSource.subquery)&&(!table.getAttributes().isEmpty())){
360                            // 当 subquery 不是一般的select,而是 VALUES (:hv_activity, :hv_description)时, table.getAttributes().isEmpty(),所以必须事先判断
361                            // MERGE INTO RECORDS AR
362                            //USING (VALUES (:hv_activity, :hv_description)
363
364                            attributeNode =  table.getAttributes().get(0);  // 这个attribute node 一般为 t.* 的形式
365                        }else{
366                            attributeNode =  new TAttributeNode(table.getDisplayName() + "." + column.getColumnNameOnly(),table);
367                        }
368                        // column.setSourceTable2(mergeSqlStatement,attributeNode,table);
369                        // System.out.println("Linked column from MERGE: "+table.getDisplayName()+"."+column.getColumnNameOnly());
370                        if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
371                            TBaseType.log(String.format("Add column %s to attribute node <%s> in the merge using table: %s", column.getColumnNameOnly(), attributeNode.getName(), table.getDisplayName()),TLog.DEBUG,node.getStartToken());
372                        }
373                        int originalSize = table.getAttributes().size();
374                        TAttributeNode.addNodeToList(attributeNode,table.getAttributes());
375                        resolver.reportNewColumns(table.getAttributes().size() - originalSize);
376                       // attributeNode.addAttributeRefToThisNode(column);
377                        // table.getAttributes().add(new TAttributeNode(table.getDisplayName() + "." + column.getColumnNameOnly(),table));
378                    }
379                }
380            }
381        }
382
383    }
384
385    public void postVisit(TMergeInsertClause node){
386        TStmtScope current = (TStmtScope)scopeStack.peek();
387    }
388
389    public void preVisit(TCTE cte) {
390        if (cte.getColumnList() != null){
391
392            int originalSize = cte.size();
393            cte.initAttributesFromColumnList();
394
395            resolver.reportNewColumns(cte.size() - originalSize);
396        }
397    }
398
399    public void postVisit(TCTE cte) {
400        if (cte.getColumnList() == null){
401            int originalSize = cte.size();
402            cte.initAttributesFromSubQuery();
403            resolver.reportNewColumns(cte.size() - originalSize);
404        }
405    }
406
407    public void postVisit(TJoinExpr joinExpr){
408        int originalSize = joinExpr.size();
409        joinExpr.initAttributes(1);
410        resolver.reportNewColumns(joinExpr.size() - originalSize);
411    }
412
413
414    public void preVisit(TSelectSqlStatement stmt) {
415        TStmtScope selectScope = new TStmtScope(scopeStack.peek(),stmt);
416        scopeStack.push(selectScope);
417    }
418
419
420    public void postVisit(TSelectSqlStatement stmt) {
421        scopeStack.pop();
422    }
423
424    public void preVisit(TMergeSqlStatement stmt) {
425        TStmtScope mergeScope = new TStmtScope(scopeStack.peek(),stmt);
426        scopeStack.push(mergeScope);
427    }
428
429    public void postVisit(TMergeSqlStatement stmt) {
430        scopeStack.pop();
431    }
432
433    public void preVisit(TCreateTableSqlStatement stmt) {
434        TStmtScope createtableScope = new TStmtScope(scopeStack.peek(),stmt);
435        scopeStack.push(createtableScope);
436    }
437
438
439    public void postVisit(TCreateTableSqlStatement stmt) {
440        scopeStack.pop();
441    }
442
443
444}