001package gudusoft.gsqlparser.nodes; 002 003import gudusoft.gsqlparser.*; 004 005import java.util.ArrayList; 006 007/** 008 * Represents a JOIN expression in SQL statements. This class replaces {@link TJoin} as of version 2.7.4.0. 009 * 010 * <p>A JOIN expression consists of: 011 * <ul> 012 * <li>A left table/subquery ({@link #getLeftTable()}) 013 * <li>A right table/subquery ({@link #getRightTable()}) 014 * <li>A join type ({@link #getJointype()}) - e.g. INNER, LEFT, RIGHT, FULL, CROSS 015 * <li>An optional ON condition ({@link #getOnCondition()}) 016 * <li>Optional USING columns ({@link #getUsingColumns()}) 017 * </ul> 018 * 019 * <p>For nested joins, the left and right operands can themselves be JOIN expressions, 020 * allowing representation of complex join trees. Use {@link #getLeftMostJoinExpr()} to 021 * traverse to the leftmost join in a nested structure. 022 * 023 * <p>PostgreSQL-specific: Supports USING clause aliases via {@link #getJoin_using_alias()}. 024 * 025 * <p>The class implements {@link IRelation} and maintains a list of available attributes 026 * from both sides of the join via {@link #getAttributes()}. 027 * 028 * <p>Represents join table in parse tree. 029*/ 030public class TJoinExpr extends TNodeWithAliasClause implements IRelation { 031 032 private TAliasClause join_using_alias; 033 034 /** 035 * Postgres: USING ( join_column [, ...] ) [ AS join_using_alias ] 036 * @return 037 */ 038 public TAliasClause getJoin_using_alias() { 039 return join_using_alias; 040 } 041 042 public void initAttributes(int roundNo){ 043 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){ 044 TBaseType.log(String.format("Starting prepare attributes (num = %d) in round %s from join expr", relationAttributes.size() , roundNo),TLog.DEBUG,this.getStartToken()); 045 } 046 047 TAttributeNode.addAllNodesToList(leftTable.getAttributes(),relationAttributes); 048 TAttributeNode.addAllNodesToList(rightTable.getAttributes(),relationAttributes); 049// relationAttributes.addAll(leftTable.getAttributes()); 050// relationAttributes.addAll(rightTable.getAttributes()); 051 052 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){ 053 TBaseType.log(String.format("Prepare attributes (num = %d) from join expr:%s", relationAttributes.size() ,this.toString()),TLog.DEBUG,this.getStartToken()); 054 } 055 int c = 0; 056 for(TAttributeNode node:relationAttributes){ 057 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){ 058 TBaseType.log(String.format("\t Attribute: %s",node.getName()),TLog.DEBUG,node.getTable_ref().getStartToken()); 059 } 060 c++; 061 if (c >TLog.OUTPUT_ATTRIBUTES_MAX){ 062 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){ 063 TBaseType.log(String.format("\t...skipped after output %d attributes",c-1),TLog.DEBUG,this.getStartToken()); 064 } 065 break; 066 } 067 } 068 } 069 070 ArrayList<TAttributeNode> relationAttributes = new ArrayList<>(); 071// public ArrayList<TObjectName> getReferenceAttributes(){ 072// return null; 073// } 074 @Override 075 public ArrayList<TAttributeNode> getAttributes(){ 076 return relationAttributes; 077 } 078 079 @Override 080 public String getRelationName(){ 081 if (this.getAliasClause() != null) return this.getAliasClause().toString(); 082 else return null; 083 } 084 085 @Override 086 public int size(){ 087 return relationAttributes.size(); 088 } 089 090 private TTable leftTable; 091 public void setLeftTable(TTable leftTable) { 092 this.leftTable = leftTable; 093 094 } 095 public void setRightTable(TTable rightTable) { 096 this.rightTable = rightTable; 097 098 } 099 100 public TTable getLeftTable() { 101 return leftTable; 102 } 103 104 public TTable getRightTable() { 105 return rightTable; 106 } 107 108 private TTable rightTable; 109 110 private int nestedParen = 0; 111 112 public void incNestedParen(){ 113 nestedParen++; 114 } 115 public int getNestedParen() { 116 return nestedParen; 117 } 118 119 public TObjectNameList getUsingColumns() { 120 return usingColumns; 121 } 122 123 public TExpression getOnCondition() { 124 return onCondition; 125 } 126 127 /** 128 * using (column list) 129 */ 130 public TObjectNameList usingColumns; 131 132 /** 133 * on condition; 134 */ 135 public TExpression onCondition; 136 137 public EJoinType getJointype() { 138 return jointype; 139 } 140 141 private EJoinType jointype; 142 public EJoinType original_jontype; // used when this join was nested by () 143 144 public void setJointype(EJoinType jointype) { 145 this.jointype = jointype; 146 } 147 148 private TFromTable leftOperand; 149 150 /*** 151 * This method is used by parser, do not use it in your code. 152 * After parsing, you can use getLeftTable() instead. 153 */ 154 public TFromTable getLeftOperand() { 155 return leftOperand; 156 } 157 158 /*** 159 * This method is used by parser, do not use it in your code. 160 * After parsing, you can use getRightTable() instead. 161 */ 162 public TFromTable getRightOperand() { 163 return rightOperand; 164 } 165 166 private TFromTable rightOperand; 167 168 /*** 169 * This method is used by parser, do not use it in your code. 170 * After parsing, you can use setLeftTable() instead. 171 */ 172 public void setLeftOperand(TFromTable leftOperand) { 173 this.leftOperand = leftOperand; 174 } 175 176 /*** 177 * This method is used by parser, do not use it in your code. 178 * After parsing, you can use setRightTable() instead. 179 */ 180 public void setRightOperand(TFromTable rightOperand) { 181 this.rightOperand = rightOperand; 182 } 183 184 //private TJoinExpr joinExpr; //valid only when jointype = nested 185 186 public void init(Object arg1) 187 { 188 // this.joinExpr = (TJoinExpr)arg1; 189 this.jointype = EJoinType.nested; 190 } 191 192 public void init(Object arg1, Object arg2) 193 { 194 this.leftOperand = (TFromTable)arg1; 195 this.rightOperand = (TFromTable)arg2; 196 } 197 198 199 public void setJoinCondition(TDummy pDummy){ 200 if (pDummy == null) return; 201 if (pDummy.node1 != null){ 202 this.onCondition = (TExpression)(pDummy.node1); 203 } 204 205 else if (pDummy.list1 != null){ 206 this.usingColumns = (TObjectNameList)(pDummy.list1); 207 } 208 209 if (pDummy.node2 != null){ 210 this.join_using_alias = (TAliasClause) pDummy.node2; 211 } 212 } 213 214 public void setJoinCondition(TExpression onCondition){ 215 this.onCondition = onCondition; 216 } 217 218 public TJoinExpr getLeftMostJoinExpr(){ 219 TJoinExpr ret = this; 220 TFromTable fromTable = leftOperand; 221 while (fromTable != null){ 222 if (fromTable.getFromtableType() != ETableSource.join) break; 223 ret = fromTable.getJoinExpr(); 224 fromTable = ret.getLeftOperand(); 225 } 226 227 return ret; 228 } 229 230 public void setNestedParen(int nestedParen) { 231 this.nestedParen = nestedParen; 232 } 233 234 public void setUsingColumns(TObjectNameList usingColumns) { 235 this.usingColumns = usingColumns; 236 } 237 238 public void setOnCondition(TExpression onCondition) { 239 this.onCondition = onCondition; 240 } 241 242 private TExpression matchCondition; 243 244 public TExpression getMatchCondition() { 245 return matchCondition; 246 } 247 248 public void setMatchCondition(TExpression matchCondition) { 249 this.matchCondition = matchCondition; 250 } 251 252 253 254 public void accept(TParseTreeVisitor v){ 255 v.preVisit(this); 256 v.postVisit(this); 257 } 258 259 public void acceptChildren(TParseTreeVisitor v) { 260 v.preVisit(this); 261 leftTable.acceptChildren(v); 262 rightTable.acceptChildren(v); 263 if (onCondition != null) onCondition.acceptChildren(v); 264 if (usingColumns != null) usingColumns.acceptChildren(v); 265 v.postVisit(this); 266 } 267}