001package gudusoft.gsqlparser.resolver; 002 003import gudusoft.gsqlparser.*; 004import gudusoft.gsqlparser.compiler.TContext; 005import gudusoft.gsqlparser.compiler.TScope; 006import gudusoft.gsqlparser.nodes.*; 007import gudusoft.gsqlparser.stmt.TSelectSqlStatement; 008 009import java.util.ArrayList; 010import java.util.Stack; 011 012 013/** 014 * TStarColumnPushDownResolver 类负责解析和处理 SQL 查询中的星号(*)列引用, 015 * 将星号列引用追溯到实际的表列。 016 * 017 * 主要工作流程: 018 * 1. 从顶层查询开始,向下遍历所有子查询 019 * 2. 对每个查询中的星号列,找到其对应的实际列 020 * 3. 将这些列信息传递给下层查询 021 * 4. 最终将星号列解析到实际的表列 022 * 023 * 关键依赖类: 024 * - TContext: 提供全局作用域信息 025 * - TScope: 管理查询作用域 026 * - TSelectSqlStatement: 表示 SELECT 语句 027 * - TTable: 表示表引用 028 * - TResultColumn: 表示结果列 029 * - TObjectName: 表示对象名称 030 * - TAttributeNode: 表示属性节点 031 */ 032public class TStarColumnPushDownResolver extends TParseTreeVisitor { 033 034 /** 035 * 类的成员变量 036 */ 037 private TContext globalScope; // 全局作用域 038 private TStatementList sqlStatements; // SQL 语句列表 039 private Stack<TScope> scopeStack = new Stack<>(); // 作用域栈 040// private Set<Integer> visitedStatements = new HashSet<>(); // Track already visited statements 041 042 /** 043 * 构造函数 044 * @param sqls SQL语句列表 045 * @param scope 全局作用域 046 */ 047 public TStarColumnPushDownResolver(TStatementList sqls, TContext scope) { 048 this.globalScope = scope; 049 sqlStatements = sqls; 050 scopeStack.push(globalScope); 051 } 052 053 /** 054 * 开始解析过程 055 * 遍历所有 SQL 语句,对每个语句执行星号列下推解析 056 */ 057 public void resolve() { 058 // System.out.println("==== Start resolving star column:"); 059 060 for (int i = 0; i < sqlStatements.size(); i++) { 061 //System.out.println("\n==== Start SQL: "+ (i+1)); 062 sqlStatements.get(i).acceptChildren(this); 063 } 064 } 065 066 /** 067 * 处理 SELECT 语句中的星号列 068 * 069 * 工作流程: 070 * 1. 检查是否已处理过该语句 071 * 2. 处理 UNION 等集合操作的情况 072 * 3. 遍历结果列表中的星号列 073 * 4. 对每个星号列: 074 * - 检查是否有精确匹配的列 075 * - 处理多个表的星号情况 076 * - 根据表类型(实体表/子查询)进行相应处理 077 * 078 * @param stmt SELECT 语句对象 079 * 080 * 示例: 081 * SELECT col_1, col_11, col_2 082 * FROM ( 083 * select * 084 * FROM (select * from table_a) ta 085 * ) a 086 * LEFT OUTER JOIN ( 087 * select id, col_2 088 * FROM table_b 089 * ) b on a.id = b.id 090 */ 091 public void preVisit(TSelectSqlStatement stmt) { 092 093 // Check if we've already processed this statement instance 094// int stmtId = System.identityHashCode(stmt); 095// if (visitedStatements.contains(stmtId)) { 096// System.out.println("Statement already visited: " + stmt.getClass().getName() + "@" + Integer.toHexString(stmtId)); 097// return; 098// } 099// visitedStatements.add(stmtId); 100 101 // 把 select list 中 star column 包含的由上层传递下来的 column 关联到本语句 from clause 的 table中 102 // 如果 table 类型为 real table,直接把column关联给该table 103 // 如果 table 类型为 sub-query, 104// for (TTable table : stmt.getRelations()) { 105// TBaseType.log(String.format("List attribute nodes for %s", table.getDisplayName()), TLog.DEBUG, table.getTableName()); 106// int i = 0; 107// for (TAttributeNode node : table.getAttributes()) { 108// TBaseType.log(String.format("\t%d: %s, sub result column %s", i, node.getName(), (node.getSubLevelResultColumn() != null) ? node.getSubLevelResultColumn().toString() : "null"), TLog.DEBUG); 109// i++; 110// } 111// } 112 113 // cte 中的 sub-query 会被遍历两次,第一次只有一个 cte 的 subQuery 会被处理, 114 // 因为还没有从 up level push down column 到 star column,其他的cte subquery 不会被处理 115 // 在 postVisit(TSelectSqlStatement stmt) 进行第二次遍历时,其他的cte subquery 才被处理 116 // 为防止已经被处理的那个 cte subquery 重复处理,加这个判断 117 if (stmt.isStarColumnPushedDown()) return; 118 119 if (stmt.getResultColumnList() == null) return; 120 121 if (stmt.isCombinedQuery()) { 122 // 如果select是union等set操作,初始状态下只要 right stmt result column 中包含的上层传下来的getAttributesReferenceToStarColumn() 123 // 需要传递给 left stmt。 这个行为每次进入 select stmt都会执行,依次往下层走,是递归的,因此在这里每次只要处理本层的即可。 124 125 TSelectSqlStatement farLeftStmt = stmt.getFarLeftStmt(); 126 TSelectSqlStatement rightStmt = stmt.getRightStmt(); 127 if (farLeftStmt != null && rightStmt != null) { 128 TResultColumnList leftList = farLeftStmt.getResultColumnList(); 129 TResultColumnList rightList = rightStmt.getResultColumnList(); 130 int k = 0; 131 if (leftList != null && rightList != null){ 132 for (TResultColumn r : leftList) { 133 k++; 134 if (r.getAttributesFromUpLevelReferenceToStarColumn().size() == 0) continue; 135 // System.out.println("Size of rightList: " + r.getAttributesFromUpLevelReferenceToStarColumn().size()); 136 if (k<rightList.size()) { 137 // must be clear, otherwise, there will be duplicate columns. Not sure why, 138 // please check this testcase: testPerformanceTeradata 139 if (rightList.getResultColumn(k - 1) != null) { 140 rightList.getResultColumn(k - 1).getAttributesFromUpLevelReferenceToStarColumn().clear(); 141 } 142 } 143 for (TObjectName o : r.getAttributesFromUpLevelReferenceToStarColumn()) { 144 if (k-1 <= rightList.size() - 1){ 145 rightList.getResultColumn(k - 1).getAttributesFromUpLevelReferenceToStarColumn().add(o); 146 }else{ 147 // select list in left select is not the same as the right select 148 if (rightList.getResultColumn(0).toString().equalsIgnoreCase("*")){ 149 // 使用第一个以 * 结尾的 result column 150 rightList.getResultColumn(0).getAttributesFromUpLevelReferenceToStarColumn().add(o); 151 } 152 } 153 } 154 } 155 } 156 } 157 } 158 159 //if (stmt.getResultColumnList() == null) return; 160 161 for (TResultColumn resultColumn : stmt.getResultColumnList()) { 162 if (!resultColumn.toString().endsWith("*")) continue; // 依次处理每个 * 字段,在select list中可能存在多个 * 字段, 例如 t1.*, t2.* 163 164 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){ 165 TBaseType.log(String.format("Star column push down: %s, up level columns: %d", resultColumn.toString(), resultColumn.getAttributesFromUpLevelReferenceToStarColumn().size()), TLog.DEBUG, resultColumn.getStartToken()); 166 } 167 168 TObjectName starColumn = resultColumn.getExpr().getObjectOperand(); 169 // System.out.println("All candidates attributes of star column:"+starColumn.getCandidateAttributeNodes()); 170 for (TObjectName o : resultColumn.getAttributesFromUpLevelReferenceToStarColumn()) { 171 // 依次处理从 up level 传下来的,关联到该 * 字段的每个 column 172 boolean matchedWithRegularColumn = false; 173 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){ 174 TBaseType.log(String.format("\tColumn from up level: %s", o.toString()), TLog.DEBUG, o.getStartToken()); 175 } 176 177 // 该 * 字段包含的扩展至同层 from clause 的 source column,可能的形式为 t.*, t.c, 178 // 先和 t.c 类型的 source column 进行精准匹配,如果找到,则无需往下推 179 // 另一个情况是有DDL,* 已经被成功展开,这时也是可以被match到,不会在向上寻找。 180 for (TAttributeNode attributeNode : starColumn.getAttributeNodesDerivedFromFromClause()) { 181 if (attributeNode.getName().toUpperCase().endsWith( TBaseType.getLastPartOfQualifiedName( o.toString().toUpperCase()))) { 182 matchedWithRegularColumn = true; 183 continue; 184 } 185 } 186 if (matchedWithRegularColumn) continue; 187 188 // 没有和 t.c 类型的 source column 匹配上,接下来和 t.* 类型的 source column 进行匹配 189 190 // 计算有几个 table.*, 如果有超过一个, 则无法建立正确的关联,根据 TBaseType.GUESS_COLUMN_STRATEGY 来选择 191 int count = 0; 192 TAttributeNode pickOne = null; 193 194 for (TAttributeNode attributeNode : starColumn.getAttributeNodesDerivedFromFromClause()) { 195 if (attributeNode.getName().endsWith("*")) { 196 switch (TBaseType.GUESS_COLUMN_STRATEGY) { 197 case TBaseType.GUESS_COLUMN_STRATEGY_NEAREST: 198 case TBaseType.GUESS_COLUMN_STRATEGY_NOT_PICKUP: 199 if (pickOne == null) pickOne = attributeNode; 200 break; 201 case TBaseType.GUESS_COLUMN_STRATEGY_FARTHEST: 202 pickOne = attributeNode; 203 break; 204 } 205 count++; 206 } 207 } 208 209 // 确定 从 up level 传下来的 column 关联到 本层的哪个 table 210 if (pickOne == null) { 211 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){ 212 TBaseType.log(String.format("Can't derive source columns from from clause for star column, source column is: %s", o.toString()), TLog.ERROR,o.getStartToken()); 213 } 214 return; 215 } 216 TTable sourceTableOfStarColumn = pickOne.getTable_ref(); 217 218 if (count > 1) { 219 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){ 220 TBaseType.log(String.format("Star column link to %d candidates tables, guess strategy used to pickup table is: %s, current linked table is: %s, " 221 , count 222 , TBaseType.GUESS_COLUMN_STRATEGY_MSG[TBaseType.GUESS_COLUMN_STRATEGY] 223 , (TBaseType.GUESS_COLUMN_STRATEGY == TBaseType.GUESS_COLUMN_STRATEGY_NOT_PICKUP) ? "None" : sourceTableOfStarColumn.toString()) 224 , TLog.WARNING, starColumn); 225 } 226 // add this column to orphan column list 227 o.setResolveStatus(TBaseType.RESOLVED_BUT_AMBIGUOUS); 228 stmt.getAncestorStmt().getOrphanColumns().addObjectName(o); 229 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){ 230 TBaseType.log(String.format("Add orphan column <%s> to statement in star column push down resolver in stmt", o.toString()), TLog.WARNING, stmt.getAncestorStmt()); 231 } 232 } 233 234 if ((count == 1) 235 || (TBaseType.GUESS_COLUMN_STRATEGY == TBaseType.GUESS_COLUMN_STRATEGY_NEAREST) 236 || (TBaseType.GUESS_COLUMN_STRATEGY == TBaseType.GUESS_COLUMN_STRATEGY_FARTHEST) 237 ) { 238 239 stmt.setStarColumnPushedDown(true); 240 switch (sourceTableOfStarColumn.getTableType()) { 241 case objectname: 242 if (sourceTableOfStarColumn.isCTEName()) { 243 for (TAttributeNode node : sourceTableOfStarColumn.getAttributes()) { 244 node.addAttributeRefToThisNode(o); 245 } 246 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){ 247 TBaseType.log(String.format("Push down column %s through CTE %s", o.toString(), sourceTableOfStarColumn.toString()), TLog.DEBUG, sourceTableOfStarColumn.getTableName()); 248 } 249 } else { 250 sourceTableOfStarColumn.getLinkedColumns().addObjectName(o,true); 251 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){ 252 TBaseType.log(String.format("link column %s pushed down to normal table: %s", o.toString(), sourceTableOfStarColumn.toString()), TLog.DEBUG, sourceTableOfStarColumn.getTableName()); 253 } 254 } 255 break; 256 case subquery: 257 // 如果 table 是 sub-query 类型,把从 up level 传下来的 column 关联到 sub-query 中去 258 // 当遍历到 sub-query 时,可递归继续处理,直到找到关联的 real table 259 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){ 260 TBaseType.log(String.format("Push down column %s through subquery %s", o.toString(), sourceTableOfStarColumn.getAliasName()), TLog.DEBUG, sourceTableOfStarColumn.getTableName()); 261 } 262 for (TAttributeNode node : sourceTableOfStarColumn.getAttributes()) { 263 node.addAttributeRefToThisNode(o); // push down column 264 } 265 break; 266 } 267 } 268 } 269 270 } 271 } 272 273 /** 274 * 处理 CTE (Common Table Expression) 中的星号列 275 * 276 * 工作流程: 277 * 1. 检查是否存在 CTE 278 * 2. 从后向前遍历 CTE 列表 279 * 3. 对每个 CTE: 280 * - 处理其子查询中的星号列 281 * - 特别处理 UNION 等集合操作的情况 282 * 283 * @param stmt SELECT 语句对象 284 */ 285 public void postVisit(TSelectSqlStatement stmt) { 286 // CTE list 的每个 CTE 遍历次序按照sql的书写次序为从前到后,但是 star column 关联的column push down 需要从后往前推断, 287 // 因为后面的 cte from clause 可能引用前面 cte。 所有在这里遍历 CTE list,从后往前 push down star column 中关联的 column 288 // 以便关联到最后的真实table 289 290 // Skip if we've already processed this statement instance 291// int stmtId = System.identityHashCode(stmt); 292// if (!visitedStatements.contains(stmtId)) { 293// visitedStatements.add(stmtId); 294// } 295 296 // push down column in star column 297 if (stmt.getCteList() == null) return; 298 299 300 for(int i=stmt.getCteList().size()-1;i>=0;i--){ 301 TCTE cte = stmt.getCteList().getCTE(i); 302 if (cte.getSubquery() == null) continue; 303 //System.out.println("CTE: "+cte.getTableName().toString()); 304 if (!cte.getSubquery().isCombinedQuery()){ 305 this.preVisit(cte.getSubquery()); 306 }else{ 307 // 如果是多个select 组成的 union集合,一开始只有最后一个 select 中有从上传 push down下来的数据 308 // 因此,需要把这些数据复制到每一个select中 309 ArrayList<TSelectSqlStatement> list = cte.getSubquery().getFlattenedSelects(); 310 if (list == null || list.isEmpty()) continue; 311 TSelectSqlStatement last = list.get(0); //list.get(list.size()-1); 312 if (last == null) continue; 313 314 if (last.getResultColumnList() == null) continue; 315 316 int k = 0; 317 for (TResultColumn r : last.getResultColumnList()) { 318 k++; 319 if (r.getAttributesFromUpLevelReferenceToStarColumn().size() == 0) continue; 320 for (TObjectName o : r.getAttributesFromUpLevelReferenceToStarColumn()) { 321 //for(int j=list.size()-2;j>=0;j--){ 322 for(int j=1;j<list.size();j++){ 323 TSelectSqlStatement selectSqlStatement = list.get(j); 324 if (selectSqlStatement == null) continue; 325 if (selectSqlStatement.getResultColumnList() == null) continue; 326 if (selectSqlStatement.getResultColumnList().getResultColumn(k - 1) == null) continue; 327 if (selectSqlStatement.getResultColumnList().getResultColumn(k - 1).getAttributesFromUpLevelReferenceToStarColumn() == null) continue; 328 selectSqlStatement.getResultColumnList().getResultColumn(k - 1).getAttributesFromUpLevelReferenceToStarColumn().add(o); 329 } 330 } 331 } 332 333 for(int k1=list.size()-1;k1>=0;k1--){ 334 TSelectSqlStatement selectStmt = list.get(k1); 335 if (selectStmt != null) { 336 this.preVisit(selectStmt); // 准备好 result column 中的 star column数据后,开始执行push down 337 } 338 } 339 } 340 } 341 } 342}