001package gudusoft.gsqlparser.stmt;
002
003import gudusoft.gsqlparser.*;
004import gudusoft.gsqlparser.nodes.*;
005import gudusoft.gsqlparser.nodes.couchbase.TUseKeyIndex;
006import gudusoft.gsqlparser.nodes.mssql.TOptionClause;
007import gudusoft.gsqlparser.nodes.oracle.TErrorLoggingClause;
008
009import java.util.ArrayList;
010
011/**
012 * SQL update statement.
013 * <br>
014 * <br> {@link #getTargetTable} returns the target table, this table can also be fetched from the first element of  {@link #tables}.
015 * <br> If there is a from clause in update statement, {@link #joins} returns all the tables in the from clause.
016 * <br> {@link #getResultColumnList()} returns assignment in set clause.
017 *
018 *<pre>
019 * UPDATE dbo.Table2
020 * SET dbo.Table2.ColB = dbo.Table2.ColB + dbo.Table1.ColB
021 * FROM dbo.Table2
022 * INNER JOIN dbo.Table1
023 * ON (dbo.Table2.ColA = dbo.Table1.ColA);
024 *</pre>
025 * Table: dbo.Table2 can be fetched from {@link #getTargetTable} or {@link #tables}
026 * <br>set clause: dbo.Table2.ColB = dbo.Table2.ColB + dbo.Table1.ColB, {@link #getResultColumnList}
027 * <br>from clause: dbo.Table2 inner join , {@link #joins}
028 *
029 *
030 * @see TCustomSqlStatement#cteList
031 * @see TCustomSqlStatement#topClause
032 * @see TCustomSqlStatement#targetTable
033 * @see TCustomSqlStatement#outputClause
034 * @see TCustomSqlStatement#joins
035 * @see TCustomSqlStatement#resultColumnList 
036 * @see TCustomSqlStatement#whereClause
037 * @see TCustomSqlStatement#returningClause
038 */
039
040public class TUpdateSqlStatement extends TCustomSqlStatement {
041
042
043    private TInsertSqlStatement insertSqlStatement = null;
044
045    /**
046     * Teradata, insert statement used after ELSE keyword
047     * @return insert statement used after ELSE keyword
048     */
049    public TInsertSqlStatement getInsertSqlStatement() {
050        return insertSqlStatement;
051    }
052
053    private TErrorLoggingClause errorLoggingClause;
054
055    /**
056     * Oracle, error logging clause
057     * @return Oracle, error logging clause
058     */
059    public TErrorLoggingClause getErrorLoggingClause() {
060        return errorLoggingClause;
061    }
062
063    private TOptionClause optionClause;
064
065    /**
066     * sql server option clause
067     * @return option clause
068     *
069     * @see gudusoft.gsqlparser.nodes.mssql.TOptionClause
070     */
071    public TOptionClause getOptionClause() {
072        return optionClause;
073    }
074
075    private TSourceToken updateToken = null;
076
077    public void setUpdateToken(TSourceToken updateToken) {
078        this.updateToken = updateToken;
079    }
080
081    /**
082     *  UPDATE keyword
083     *
084     * @return UPDATE keyword in update statement.
085     */
086    public TSourceToken getUpdateToken() {
087
088        return updateToken;
089    }
090
091    private TJoinList referenceJoins = null;
092
093    /**
094     * @deprecated As of v1.9.7.2, use {@link #joins} instead.
095     * <br>
096     * <p>  getReferenceJoins() represents: table_references
097     * @return table references in from clause
098     */
099    public TJoinList getReferenceJoins() {
100        if (this.referenceJoins == null){
101            this.referenceJoins = new TJoinList();
102        }
103        return referenceJoins;
104    }
105
106    private TOrderBy orderByClause = null;
107
108    /**
109     * Couchbase, MySQL limit clause.
110     * @return Couchbase, MySQL limit clause.
111     */
112    public TLimitClause getLimitClause() {
113        return limitClause;
114    }
115
116    /**
117     * Order by clause is not used.
118     * @return Order by clause
119     */
120    public TOrderBy getOrderByClause() {
121        return orderByClause;
122    }
123
124    private TLimitClause limitClause = null;
125
126    public TUpdateSqlStatement(EDbVendor dbvendor) {
127        super(dbvendor);
128        sqlstatementtype = ESqlStatementType.sstupdate;
129    }
130
131    void buildsql() {
132    }
133
134    void clear() {
135    }
136
137    String getasprettytext() {
138        return "";
139    }
140
141    void iterate(TVisitorAbs pvisitor) {
142    }
143
144    public int doParseStatement(TCustomSqlStatement psql) {
145        if (rootNode == null) return -1;
146        TUpdateSqlNode updateNode = (TUpdateSqlNode)rootNode;
147
148        if (this.sourcetokenlist.size() == 0){
149            // subquery nested in other statements.
150            this.setStartToken(updateNode.getStartToken());
151            this.setEndToken(updateNode.getEndToken());
152        }
153
154        super.doParseStatement(psql);
155        this.updateToken = updateNode.getUpdateToken();
156        
157        if (updateNode.cteList != null){
158            this.setCteList(updateNode.cteList);
159            this.getCteList().doParse(this, ESqlClause.cte);
160        }
161
162        if (updateNode.getTopClause() != null){
163           updateNode.getTopClause().doParse(this,ESqlClause.top);
164           this.setTopClause(updateNode.getTopClause());
165        }
166
167        if (updateNode.getSourceTableList() != null){ // update ... from source table list
168            TFromTable lcFromTable = null;
169            TJoin lcJoin = null;
170
171            for(int i=0; i<updateNode.getSourceTableList().size();i++){
172                lcFromTable = updateNode.getSourceTableList().getFromTable(i);
173                if (lcFromTable.getFromtableType() != ETableSource.join){
174                    lcJoin = new TJoin();
175                    TTable lcTable =  analyzeFromTable(lcFromTable,true);
176                    lcTable.setEffectType(ETableEffectType.tetSelect);
177                    lcJoin.setTable(lcTable);
178                    this.getRelations().add(lcTable);
179                }else{
180                    this.fromSourceJoin = lcFromTable.getJoinExpr();
181
182                    this.fromSourceTable = new TTable();
183                    this.fromSourceTable.setTableType(ETableSource.join);
184                    this.fromSourceTable.setAliasClause(lcFromTable.getJoinExpr().getAliasClause());
185                    this.fromSourceTable.setStartToken(lcFromTable.getStartToken());
186                    this.fromSourceTable.setEndToken(lcFromTable.getEndToken());
187                    this.fromSourceTable.setGsqlparser(lcFromTable.getGsqlparser());
188                    this.fromSourceTable.setJoinExpr(this.fromSourceJoin);
189                    this.getRelations().add(this.fromSourceTable);
190
191                    lcJoin = analyzeJoin(lcFromTable.getJoinExpr(),null,true);
192                    lcJoin.doParse(this, ESqlClause.join);
193                }
194                joins.addJoin(lcJoin);
195            }
196        }
197
198        if (updateNode.getTargetTable().getFromtableType() != ETableSource.join ){
199            // search in from clause to see if this is the table alias of table in from clause
200            boolean isTableAlias = false;
201            TTable lcLinkTable = null;
202            String targetName = updateNode.getTargetTable().toString();
203            if (getJoins().size() > 0){
204                for(int j=0;j<getJoins().size() && !isTableAlias;j++){
205                    TJoin lcJoinForAlias = getJoins().getJoin(j);
206                    TTable lcCandidate = lcJoinForAlias.getTable();
207                    if (lcCandidate != null && lcCandidate.getAliasClause() != null
208                            && targetName.equalsIgnoreCase(lcCandidate.getAliasName())){
209                        lcLinkTable = lcCandidate;
210                        isTableAlias = true;
211                        break;
212                    }
213                    // Also walk join items - the target alias may be on a joined table
214                    // (e.g. UPDATE S ... FROM A JOIN B S ON ...)
215                    if (lcJoinForAlias.getJoinItems() != null){
216                        for(int k=0;k<lcJoinForAlias.getJoinItems().size();k++){
217                            TTable lcItemTable = lcJoinForAlias.getJoinItems().getJoinItem(k).getTable();
218                            if (lcItemTable != null && lcItemTable.getAliasClause() != null
219                                    && targetName.equalsIgnoreCase(lcItemTable.getAliasName())){
220                                lcLinkTable = lcItemTable;
221                                isTableAlias = true;
222                                break;
223                            }
224                        }
225                    }
226                }
227            }
228            TTable lcTable = analyzeFromTable(updateNode.getTargetTable(),false);
229            lcTable.setEffectType(ETableEffectType.tetUpdate);
230            if (isTableAlias){
231                lcTable.setLinkTable(lcLinkTable);
232                setTargetTable(lcLinkTable);
233            }else{
234                tables.insertElementAt(lcTable,0);
235                setTargetTable(lcTable);
236                this.getRelations().add(0,lcTable); // update table 需要放在 from table 前
237            }
238            //setTargetTable(lcTable);
239        }else{
240            this.fromSourceJoin = updateNode.getTargetTable().getJoinExpr();
241
242            this.fromSourceTable = new TTable();
243            this.fromSourceTable.setTableType(ETableSource.join);
244            this.fromSourceTable.setAliasClause(updateNode.getTargetTable().getJoinExpr().getAliasClause());
245            this.fromSourceTable.setStartToken(updateNode.getTargetTable().getJoinExpr().getStartToken());
246            this.fromSourceTable.setEndToken(updateNode.getTargetTable().getJoinExpr().getEndToken());
247            this.fromSourceTable.setGsqlparser(updateNode.getTargetTable().getJoinExpr().getGsqlparser());
248            this.fromSourceTable.setJoinExpr(this.fromSourceJoin);
249            this.getRelations().add(0,this.fromSourceTable); // update table 需要放在 from table 前
250
251            TJoin  lcJoin1 = analyzeJoin(updateNode.getTargetTable().getJoinExpr(),null,true);
252            lcJoin1.doParse(this, ESqlClause.join);
253            setTargetTable(lcJoin1.getTable());
254            joins.addJoin(lcJoin1);
255        }
256
257        if (updateNode.getUseKeyIndex() != null){
258            setUseKeyIndex(updateNode.getUseKeyIndex());
259            getUseKeyIndex().doParse(this,ESqlClause.unknown);
260        }
261
262        if (updateNode.getOutputClause() != null){
263            updateNode.getOutputClause().doParse(this,ESqlClause.output);
264            this.setOutputClause(updateNode.getOutputClause());
265        }
266
267        setResultColumnList(updateNode.getResultColumnList());
268
269        if (updateNode.getUnSetTerms() != null){
270            setUnSetTerms(updateNode.getUnSetTerms());
271            getUnSetTerms().doParse(this,ESqlClause.unSet);
272        }
273
274
275        if (updateNode.getReferenceTableList() != null){
276            // Postgresql syntax:
277            // update table_name1 set f=1
278            // from table_references
279            TFromTable lcFromTable = null;
280            TJoin lcJoin = null;
281
282            for(int i=0; i<updateNode.getReferenceTableList().size();i++){
283                lcFromTable = updateNode.getReferenceTableList().getFromTable(i);
284                if (lcFromTable.getFromtableType() != ETableSource.join){
285                    lcJoin = new TJoin();
286                    TTable lcTable = analyzeFromTable(lcFromTable,true);
287                    lcTable.setEffectType(ETableEffectType.tetSelect);
288                    lcJoin.setTable( lcTable);
289                    this.getRelations().add(lcTable);
290                }else{
291                    this.fromSourceJoin = lcFromTable.getJoinExpr();
292
293                    this.fromSourceTable = new TTable();
294                    this.fromSourceTable.setTableType(ETableSource.join);
295                    this.fromSourceTable.setAliasClause(lcFromTable.getJoinExpr().getAliasClause());
296                    this.fromSourceTable.setStartToken(lcFromTable.getJoinExpr().getStartToken());
297                    this.fromSourceTable.setEndToken(lcFromTable.getJoinExpr().getEndToken());
298                    this.fromSourceTable.setGsqlparser(lcFromTable.getJoinExpr().getGsqlparser());
299                    this.fromSourceTable.setJoinExpr(this.fromSourceJoin);
300                    this.getRelations().add(this.fromSourceTable);
301
302                    lcJoin = analyzeJoin(lcFromTable.getJoinExpr(),null,true);
303                    lcJoin.doParse(this, ESqlClause.join);
304                }
305              //  this.getReferenceJoins().addJoin(lcJoin);
306                joins.addJoin(lcJoin);
307            }
308
309        }
310
311        if (getResultColumnList() != null){
312            getResultColumnList().doParse(this,ESqlClause.set);
313        }
314
315        if (updateNode.getOrderByClause() != null){
316            setOrderByClause(updateNode.getOrderByClause());
317            getOrderByClause().doParse(this,ESqlClause.orderby);
318        }
319
320        if (updateNode.getLimitClause() != null){
321            setLimitClause(updateNode.getLimitClause());
322            getLimitClause().doParse(this,ESqlClause.limit);
323        }
324
325        if (updateNode.getWhereCondition() != null){
326            updateNode.getWhereCondition().doParse(this,ESqlClause.where);
327            this.setWhereClause(updateNode.getWhereCondition());
328        }
329
330        if (updateNode.getReturningClause() != null){
331            updateNode.getReturningClause().doParse(this,ESqlClause.returning);
332            this.setReturningClause(updateNode.getReturningClause());
333        }
334
335        this.optionClause = updateNode.getOptionClause();
336
337        errorLoggingClause = updateNode.getErrorLoggingClause();
338
339        if (updateNode.getInsertSqlNode() != null){
340            insertSqlStatement = new TInsertSqlStatement(this.dbvendor);
341            insertSqlStatement.rootNode = updateNode.getInsertSqlNode();
342            insertSqlStatement.doParseStatement(this);
343
344        }
345
346        // remove table alias A from tables like this SQL:
347        // delete  A
348        // from  myTable A
349        // join otherTable B on A.Id=B.Id
350        int deletedTables[] = new int[this.tables.size()];
351        TTable lcTable = null,lcTable2 = null;
352        for(int i=0;i<this.tables.size();i++){
353            lcTable = this.tables.getTable(i);
354            if ((lcTable.getAliasClause() == null)&&(lcTable.isBaseTable())){
355                for(int j=0;j<this.tables.size();j++){
356                    if (i == j) {continue;}
357                    lcTable2 = this.tables.getTable(j);
358                    if (lcTable2.getAliasClause() != null){
359                        if (lcTable2.getAliasClause().toString().compareToIgnoreCase(lcTable.toString()) == 0){
360                            deletedTables[i] = 1;
361                            //lcTable.setLinkTable(true);
362                            lcTable.setLinkTable(lcTable2);
363//                            for(int k=0;k<lcTable.getObjectNameReferences().size();k++){
364//                                lcTable2.getObjectNameReferences().addObjectName(lcTable.getObjectNameReferences().getObjectName(k));
365//                                //lcTable2.getLinkedColumns().addObjectName(lcTable.getObjectNameReferences().getObjectName(k));
366//                                //lcTable.getObjectNameReferences().getObjectName(k).setSourceTable(lcTable2);
367//                               // System.out.println(lcTable.getObjectNameReferences().getAliasName(k).toString());
368//                            }
369                        }
370                    }
371
372                }
373            }
374        }
375//        for(int i = this.tables.size()-1;i>=0;i--){
376//            if (deletedTables[i] == 1){
377//                //this.tables.removeElementAt(i);
378//            }
379//        }
380        
381        return 0;
382    }
383
384    public void accept(TParseTreeVisitor v){
385        v.preVisit(this);
386        v.postVisit(this);
387    }
388
389    public void acceptChildren(TParseTreeVisitor v){
390        v.preVisit(this);
391
392        if (this.getCteList() != null){
393            this.getCteList().acceptChildren(v);
394        }
395
396
397
398        if (TBaseType.USE_JOINEXPR_INSTEAD_OF_JOIN){
399
400            for(TTable table:getRelations()){
401                table.acceptChildren(v);
402            }
403
404        }else{
405            this.getTargetTable().acceptChildren(v);
406            if (this.joins.size() > 0){
407                this.joins.acceptChildren(v);
408            }
409        }
410
411        if (this.getTopClause() != null){
412            this.getTopClause().acceptChildren(v);
413        }
414
415        if (this.getOutputClause() != null){
416            this.getOutputClause().acceptChildren(v);
417        }
418
419        if (this.getResultColumnList() != null){
420            this.getResultColumnList().acceptChildren(v);
421        }else{
422            TBaseType.log("set clause in update stmt is null",TLog.WARNING);
423        }
424
425
426        if (this.getWhereClause() != null){
427            this.getWhereClause().acceptChildren(v);
428        }
429
430        if (this.getOrderByClause() != null){
431            this.getOrderByClause().acceptChildren(v);
432        }
433
434        if (this.getLimitClause() != null){
435            this.getLimitClause().acceptChildren(v);
436        }
437
438        if (this.getReturningClause() != null){
439            this.getReturningClause().acceptChildren(v);
440        }
441
442        v.postVisit(this);
443    }
444
445    public void setErrorLoggingClause(TErrorLoggingClause errorLoggingClause) {
446        this.errorLoggingClause = errorLoggingClause;
447    }
448
449    public void setOptionClause(TOptionClause optionClause) {
450        this.optionClause = optionClause;
451    }
452
453    public void setReferenceJoins(TJoinList referenceJoins) {
454        this.referenceJoins = referenceJoins;
455    }
456
457    public void setOrderByClause(TOrderBy orderByClause) {
458        this.orderByClause = orderByClause;
459    }
460
461    public void setLimitClause(TLimitClause limitClause) {
462        this.limitClause = limitClause;
463    }
464
465    private TUseKeyIndex useKeyIndex;//couchbase
466
467    public void setUseKeyIndex(TUseKeyIndex useKeyIndex) {
468        this.useKeyIndex = useKeyIndex;
469    }
470
471    public TUseKeyIndex getUseKeyIndex() {
472
473        return useKeyIndex;
474    }
475
476    private TResultColumnList unSetTerms; //couchbase
477
478    public void setUnSetTerms(TResultColumnList unSetTerms) {
479        this.unSetTerms = unSetTerms;
480    }
481
482    /**
483     * Couchbase unset clause
484     * @return Couchbase unset clause
485     */
486    public TResultColumnList getUnSetTerms() {
487
488        return unSetTerms;
489    }
490
491}