001package gudusoft.gsqlparser.nodes;
002
003import gudusoft.gsqlparser.*;
004import gudusoft.gsqlparser.stmt.TDeleteSqlStatement;
005import gudusoft.gsqlparser.stmt.TInsertSqlStatement;
006import gudusoft.gsqlparser.stmt.TSelectSqlStatement;
007import gudusoft.gsqlparser.stmt.TUpdateSqlStatement;
008
009import java.util.ArrayList;
010
011/**
012 * A common table expression permits defining a result table with a table-name that can be specified as a table name in any FROM clause of the fullselect that follows.
013 *<p> Multiple common table expressions can be specified following the single WITH keyword.
014 *<p> Each common table expression specified can also be referenced by name in the FROM clause of subsequent common table expressions.
015 *<p> Syntax:
016 * <blockquote><pre>
017 * table-name [column-name [,...n]] AS (fullselect)</pre>
018 * </blockquote>
019*/
020public class TCTE extends TTable{
021
022    private boolean recursive = false;
023
024    public void setRecursive(boolean recursive) {
025        this.recursive = recursive;
026    }
027
028    public boolean isRecursive() {
029        return recursive;
030    }
031
032    public void initAttributesFromColumnList(){
033        if (this.getColumnList() != null){
034            int i = 0;
035            if ((this.getSubquery() != null)&&(this.getSubquery().getResultColumnList() != null)){
036                if (this.getSubquery().getResultColumnList().size() != this.getColumnList().size()){
037                    if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
038                        TBaseType.log(String.format("CTE: <%s> columns list size is not the same as select list in subQuery, maybe due to list -> * ", this.getTableName().toString())
039                                ,TLog.ERROR,this.getTableName());
040                    }
041                }
042            }
043            for(TObjectName column: this.getColumnList()){
044                if ((this.getSubquery() != null)&&(this.getSubquery().getResultColumnList(true) != null)){
045                    TResultColumnList resultColumnListFromSubquery = this.getSubquery().getResultColumnList(true);
046                    //  关联 subQuery 中 select list
047                       // relationAttributes.add(new TAttributeNode( this.getTableName().toString()+"." + column.toString() ,this,this.getSubquery().getResultColumnList().getResultColumn(i) ));
048                    // Fix: When subquery has SELECT *, all CTE columns should reference the single star column
049                    TResultColumn resultColumn = null;
050                    if (resultColumnListFromSubquery.size() == 1 &&
051                            resultColumnListFromSubquery.getResultColumn(0).toString().endsWith("*")) {
052                        // All CTE columns map to the single star column
053                        resultColumn = resultColumnListFromSubquery.getResultColumn(0);
054                    } else if (i < resultColumnListFromSubquery.size()) {
055                        // Normal mapping by position
056                        resultColumn = resultColumnListFromSubquery.getResultColumn(i);
057                    }
058                    TAttributeNode.addNodeToList(new TAttributeNode( this.getTableName().toString()+"." + column.toString() ,this,resultColumn ),getAttributes());
059
060                }else{
061                        //relationAttributes.add(new TAttributeNode( this.getTableName().toString()+"." + column.toString() ,this));
062                    TAttributeNode.addNodeToList(new TAttributeNode( this.getTableName().toString()+"." + column.toString() ,this),getAttributes());
063                }
064                i++;
065            }
066            //
067            if ((this.getSubquery() != null)&&(this.getSubquery().isCombinedQuery())&&(this.getSubquery().getLeftStmt().getResultColumnList() != null)){
068                TSelectSqlStatement left = subquery.getLeftStmt();
069
070                String prefix = this.getTableName().toString()+".";
071                addNewAttributeFromSubQuery(left.getResultColumnList(),getAttributes(),prefix,this.getColumnList());
072                while (left.isCombinedQuery()){
073                    left = left.getLeftStmt();
074                    addNewAttributeFromSubQuery(left.getResultColumnList(),getAttributes(),prefix,this.getColumnList());
075                }
076            }
077        }
078
079        if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
080            TBaseType.log(String.format("Prepare attributes for CTE: <%s> via column list",this.getTableName().toString()),TLog.DEBUG,this.getTableName());
081            for(TAttributeNode node:getAttributes()){
082                TBaseType.log(String.format("\tAttribute: <%s>, Select list column: <%s>",node.getName(),node.getSubLevelResultColumn()!=null?node.getSubLevelResultColumn().toString():"N/A" ),TLog.DEBUG);
083            }
084        }
085
086    }
087    public void initAttributesFromSubQuery(){
088        if (this.getSubquery() == null) return;
089        super.initAttributesFromSubquery(this.getSubquery(),this.getTableName().toString()+".");
090
091        if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
092            TBaseType.log(String.format("Prepare attributes (num = %d) for CTE: <%s> via subQuery",getAttributes().size() ,this.getTableName().toString()),TLog.DEBUG,this.getTableName());
093        }
094        int c = 0;
095        for(TAttributeNode node:getAttributes()){
096            if (node.getSqlColumn() != null){
097                if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
098                    TBaseType.log(String.format("\tAttribute: <%s>, SQL column: <%s>",node.getName(), node.getSqlColumn()!=null?node.getSqlColumn().toString():"N/A" ),TLog.DEBUG);
099                }
100            }else{
101                if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
102                    TBaseType.log(String.format("\tAttribute: <%s>, Select list column: <%s>",node.getName(), node.getSubLevelResultColumn()!=null?node.getSubLevelResultColumn().toString():"N/A" ),TLog.DEBUG);
103                }
104            }
105            if (node.getAccompaniedAttributeNodes().size() >0 ){
106                if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
107                    TBaseType.log(String.format("\t\tAccompanied nodes: %d",node.getAccompaniedAttributeNodes().size()),TLog.DEBUG);
108                }
109                for(TAttributeNode n:node.getAccompaniedAttributeNodes()){
110                    if (n.getSqlColumn() != null){
111                        if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
112                            TBaseType.log(String.format("\t\tAttribute: <%s>, SQL column: <%s>",n.getName(), n.getSqlColumn()!=null?n.getSqlColumn().toString():"N/A" ),TLog.DEBUG);
113                        }
114                    }else{
115                        if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
116                            TBaseType.log(String.format("\t\tAttribute: <%s>, Select list column: <%s>",n.getName(), n.getSubLevelResultColumn()!=null?n.getSubLevelResultColumn().toString():"N/A" ),TLog.DEBUG);
117                        }
118                    }
119                }
120            }
121
122            c++;
123            if (c > TLog.OUTPUT_ATTRIBUTES_MAX){
124                if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE){
125                    TBaseType.log(String.format("\t...skipped after output %d attributes",c-1),TLog.DEBUG,this.getTableName().getStartToken());
126                }
127
128                break;
129            }
130        }
131    }
132
133    public String getRelationName(){
134        return this.getTableName().toString();
135    }
136
137//    public ArrayList<TAttributeNode> getAttributes(){
138//        return relationAttributes;
139//    }
140
141    private TSelectSqlNode selectNode;
142    private TSelectSqlStatement subquery;
143
144    private  TInsertSqlNode insertSqlNode;
145    private TInsertSqlStatement insertStmt;
146
147    private  TUpdateSqlNode updateSqlNode;
148    private TUpdateSqlStatement updateStmt;
149
150    private TDeleteSqlNode deleteSqlNode;
151    private TDeleteSqlStatement deleteStmt;
152
153    private TCustomSqlStatement preparableStmt;
154
155    public  boolean searchColumnInResultSet(TCustomSqlStatement pSql,TTable pTable,TObjectName pColumn,boolean pMustIn){
156      boolean lcResult = false;
157        if (getColumnList() != null){
158            for (int i=0;i<getColumnList().size();i++){
159                lcResult = getColumnList().getObjectName(i).toString().equalsIgnoreCase(pColumn.getColumnNameOnly());
160                //pColumn.setSourceColumn(getColumnList().getObjectName(i));
161                if (lcResult) break;
162            }
163            return lcResult;
164        }
165
166        TCustomSqlStatement lcStmt = pSql;
167        while (lcStmt != null){
168            if (lcStmt == getSubquery()){
169                pTable.getLinkedColumns().addObjectName(pColumn);
170                pColumn.setSourceTable(pTable);
171                lcResult = true;
172                break;
173            }
174            lcStmt = lcStmt.getParentStmt();
175        }
176
177        if (lcResult) return true;
178
179        if (getSubquery() == null) return false;
180        lcResult = getSubquery().searchColumnInResultSet(pColumn,pMustIn);
181
182        return lcResult;
183    }
184
185    /**
186     * preparable statement can be:
187     * <p> {@link #getSubquery()}
188     * <p> or{@link #getUpdateStmt()}
189     * <p> or{@link #getInsertStmt()}
190     * <p> or{@link #getDeleteStmt()}
191     * @return
192     */
193    public TCustomSqlStatement getPreparableStmt() {
194        preparableStmt = null;
195        if (subquery!=null){
196            preparableStmt = subquery;
197        }else if (updateStmt != null){
198            preparableStmt = updateStmt;
199        }else if (deleteStmt != null){
200            preparableStmt = deleteStmt;
201        }else if (insertStmt != null){
202            preparableStmt = insertStmt;
203        }
204
205        return preparableStmt;
206    }
207
208    public TUpdateSqlStatement getUpdateStmt() {
209        return updateStmt;
210    }
211
212    public TInsertSqlStatement getInsertStmt() {
213
214        return insertStmt;
215    }
216
217    public TDeleteSqlStatement getDeleteStmt() {
218
219        return deleteStmt;
220    }
221
222    /**
223     * @return table name of this common table expression.
224     */
225    public TObjectName getTableName() {
226        return tableName;
227    }
228
229    private TObjectName tableName;
230
231    public void setColumnList(TObjectNameList columnList) {
232        this.columnList = columnList;
233    }
234
235    /**
236     * fullselect of this common table expression.
237     * @return
238     */
239    public TSelectSqlStatement getSubquery() {
240        return subquery;
241    }
242
243    /**
244     * @return List of column name of this common table expression.
245
246     */
247    public TObjectNameList getColumnList() {
248        return columnList;
249    }
250
251    private TObjectNameList columnList = null;
252
253    public void init(Object arg1,Object arg2)
254    {
255       tableName = (TObjectName)arg1;
256       //tableName.parseTablename();
257       //tableName.setObjectType(TObjectName.ttobjTableCTE);
258       tableName.setDbObjectType(EDbObjectType.cte);
259
260       if (arg2 instanceof TSelectSqlNode){
261       selectNode = (TSelectSqlNode)arg2;
262       }else if (arg2 instanceof TInsertSqlNode){
263           insertSqlNode = (TInsertSqlNode)arg2;
264       }else if (arg2 instanceof TUpdateSqlNode){
265           updateSqlNode = (TUpdateSqlNode)arg2;
266       }else if (arg2 instanceof TDeleteSqlNode){
267           deleteSqlNode = (TDeleteSqlNode)arg2;
268       }
269    }
270
271    public void incParenthesisCount() {
272        if (selectNode != null){
273            selectNode.incParenthesisCount();
274        }
275    }
276
277    public void doParse(TCustomSqlStatement psql, ESqlClause plocation){
278
279        if (selectNode != null){
280            //long t = System.currentTimeMillis();
281            subquery = new TSelectSqlStatement(psql.dbvendor);
282            subquery.rootNode = selectNode;
283            subquery.setQueryOfCTE(true);
284            subquery.setCteIncludeThisStmt(this);
285            subquery.doParseStatement(psql);
286            for(TTable t:subquery.getRelations()){
287                if (t.isCTEName() && t.getCTE() == this){
288                   // System.out.println("found cte name in the same level:"+ t.getCTE().toString());
289                    t.setCTEName(false);
290                    t.setCTE(null);
291                }
292            }
293//            if (columnList == null){
294//                columnList = new TObjectNameList();
295//                for(TResultColumn resultColumn:subquery.getResultColumnList()){
296//                    columnList.addObjectName(new TObjectName());
297//
298//                }
299//            }
300            //System.out.println("Time Escaped: " + (System.currentTimeMillis() - t)+", sql size:"+ this.toString().length() );
301        }else if (insertSqlNode != null){
302            insertStmt = new TInsertSqlStatement(psql.dbvendor);
303            insertStmt.rootNode = insertSqlNode;
304            insertStmt.setCteIncludeThisStmt(this);
305            insertStmt.doParseStatement(psql);
306        }else if (deleteSqlNode != null){
307            deleteStmt = new TDeleteSqlStatement(psql.dbvendor);
308            deleteStmt.rootNode = deleteSqlNode;
309            deleteStmt.setCteIncludeThisStmt(this);
310            deleteStmt.doParseStatement(psql);
311        }else if (updateSqlNode != null){
312            updateStmt = new TUpdateSqlStatement(psql.dbvendor);
313            updateStmt.rootNode = updateSqlNode;
314            updateStmt.setCteIncludeThisStmt(this);
315            updateStmt.doParseStatement(psql);
316        }
317    }
318
319    public void accept(TParseTreeVisitor v){
320        v.preVisit(this);
321        v.postVisit(this);
322    }
323
324    public void acceptChildren(TParseTreeVisitor v){
325        v.preVisit(this);
326        if (subquery != null){
327            subquery.acceptChildren(v);
328        }
329        v.postVisit(this);
330    }
331
332    public void setSubquery(TSelectSqlStatement subquery) {
333        this.subquery = subquery;
334    }
335
336    public void setInsertStmt(TInsertSqlStatement insertStmt) {
337        this.insertStmt = insertStmt;
338    }
339
340    public void setUpdateStmt(TUpdateSqlStatement updateStmt) {
341        this.updateStmt = updateStmt;
342    }
343
344    public void setDeleteStmt(TDeleteSqlStatement deleteStmt) {
345        this.deleteStmt = deleteStmt;
346    }
347
348    public void setPreparableStmt(TCustomSqlStatement preparableStmt) {
349        this.preparableStmt = preparableStmt;
350    }
351
352    public void setTableName(TObjectName tableName) {
353        this.tableName = tableName;
354    }
355}