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}