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}