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.getReferenceTableList() != null){
199            // Postgresql syntax:
200            // update table_name1 set f=1
201            // from table_references
202            // Processed before the target table so that the alias search below can
203            // link a target that is the alias of a table in the FROM clause
204            // (e.g. Snowflake/migration-style: UPDATE a SET ... FROM real_table a JOIN ...)
205            TFromTable lcFromTable = null;
206            TJoin lcJoin = null;
207
208            for(int i=0; i<updateNode.getReferenceTableList().size();i++){
209                lcFromTable = updateNode.getReferenceTableList().getFromTable(i);
210                if (lcFromTable.getFromtableType() != ETableSource.join){
211                    lcJoin = new TJoin();
212                    TTable lcTable = analyzeFromTable(lcFromTable,true);
213                    lcTable.setEffectType(ETableEffectType.tetSelect);
214                    lcJoin.setTable( lcTable);
215                    this.getRelations().add(lcTable);
216                }else{
217                    this.fromSourceJoin = lcFromTable.getJoinExpr();
218
219                    this.fromSourceTable = new TTable();
220                    this.fromSourceTable.setTableType(ETableSource.join);
221                    this.fromSourceTable.setAliasClause(lcFromTable.getJoinExpr().getAliasClause());
222                    this.fromSourceTable.setStartToken(lcFromTable.getJoinExpr().getStartToken());
223                    this.fromSourceTable.setEndToken(lcFromTable.getJoinExpr().getEndToken());
224                    this.fromSourceTable.setGsqlparser(lcFromTable.getJoinExpr().getGsqlparser());
225                    this.fromSourceTable.setJoinExpr(this.fromSourceJoin);
226                    this.getRelations().add(this.fromSourceTable);
227
228                    lcJoin = analyzeJoin(lcFromTable.getJoinExpr(),null,true);
229                    lcJoin.doParse(this, ESqlClause.join);
230                }
231              //  this.getReferenceJoins().addJoin(lcJoin);
232                joins.addJoin(lcJoin);
233            }
234
235        }
236
237        if (updateNode.getTargetTable().getFromtableType() != ETableSource.join ){
238            // search in from clause to see if this is the table alias of table in from clause
239            boolean isTableAlias = false;
240            TTable lcLinkTable = null;
241            String targetName = updateNode.getTargetTable().toString();
242            if (getJoins().size() > 0){
243                for(int j=0;j<getJoins().size() && !isTableAlias;j++){
244                    TJoin lcJoinForAlias = getJoins().getJoin(j);
245                    TTable lcCandidate = lcJoinForAlias.getTable();
246                    if (lcCandidate != null && lcCandidate.getAliasClause() != null
247                            && targetName.equalsIgnoreCase(lcCandidate.getAliasName())){
248                        lcLinkTable = lcCandidate;
249                        isTableAlias = true;
250                        break;
251                    }
252                    // Also walk join items - the target alias may be on a joined table
253                    // (e.g. UPDATE S ... FROM A JOIN B S ON ...)
254                    if (lcJoinForAlias.getJoinItems() != null){
255                        for(int k=0;k<lcJoinForAlias.getJoinItems().size();k++){
256                            TTable lcItemTable = lcJoinForAlias.getJoinItems().getJoinItem(k).getTable();
257                            if (lcItemTable != null && lcItemTable.getAliasClause() != null
258                                    && targetName.equalsIgnoreCase(lcItemTable.getAliasName())){
259                                lcLinkTable = lcItemTable;
260                                isTableAlias = true;
261                                break;
262                            }
263                        }
264                    }
265                }
266            }
267            TTable lcTable = analyzeFromTable(updateNode.getTargetTable(),false);
268            lcTable.setEffectType(ETableEffectType.tetUpdate);
269            if (isTableAlias){
270                lcTable.setLinkTable(lcLinkTable);
271                // the linked FROM table is the real update target, so it must
272                // report the update effect, same as a directly named target
273                lcLinkTable.setEffectType(ETableEffectType.tetUpdate);
274                setTargetTable(lcLinkTable);
275            }else{
276                tables.insertElementAt(lcTable,0);
277                setTargetTable(lcTable);
278                this.getRelations().add(0,lcTable); // update table 需要放在 from table 前
279            }
280            //setTargetTable(lcTable);
281        }else{
282            this.fromSourceJoin = updateNode.getTargetTable().getJoinExpr();
283
284            this.fromSourceTable = new TTable();
285            this.fromSourceTable.setTableType(ETableSource.join);
286            this.fromSourceTable.setAliasClause(updateNode.getTargetTable().getJoinExpr().getAliasClause());
287            this.fromSourceTable.setStartToken(updateNode.getTargetTable().getJoinExpr().getStartToken());
288            this.fromSourceTable.setEndToken(updateNode.getTargetTable().getJoinExpr().getEndToken());
289            this.fromSourceTable.setGsqlparser(updateNode.getTargetTable().getJoinExpr().getGsqlparser());
290            this.fromSourceTable.setJoinExpr(this.fromSourceJoin);
291            this.getRelations().add(0,this.fromSourceTable); // update table 需要放在 from table 前
292
293            TJoin  lcJoin1 = analyzeJoin(updateNode.getTargetTable().getJoinExpr(),null,true);
294            lcJoin1.doParse(this, ESqlClause.join);
295            setTargetTable(lcJoin1.getTable());
296            joins.addJoin(lcJoin1);
297        }
298
299        if (updateNode.getUseKeyIndex() != null){
300            setUseKeyIndex(updateNode.getUseKeyIndex());
301            getUseKeyIndex().doParse(this,ESqlClause.unknown);
302        }
303
304        if (updateNode.getOutputClause() != null){
305            updateNode.getOutputClause().doParse(this,ESqlClause.output);
306            this.setOutputClause(updateNode.getOutputClause());
307        }
308
309        setResultColumnList(updateNode.getResultColumnList());
310
311        if (updateNode.getUnSetTerms() != null){
312            setUnSetTerms(updateNode.getUnSetTerms());
313            getUnSetTerms().doParse(this,ESqlClause.unSet);
314        }
315
316
317        if (getResultColumnList() != null){
318            getResultColumnList().doParse(this,ESqlClause.set);
319        }
320
321        if (updateNode.getOrderByClause() != null){
322            setOrderByClause(updateNode.getOrderByClause());
323            getOrderByClause().doParse(this,ESqlClause.orderby);
324        }
325
326        if (updateNode.getLimitClause() != null){
327            setLimitClause(updateNode.getLimitClause());
328            getLimitClause().doParse(this,ESqlClause.limit);
329        }
330
331        if (updateNode.getWhereCondition() != null){
332            updateNode.getWhereCondition().doParse(this,ESqlClause.where);
333            this.setWhereClause(updateNode.getWhereCondition());
334        }
335
336        if (updateNode.getReturningClause() != null){
337            updateNode.getReturningClause().doParse(this,ESqlClause.returning);
338            this.setReturningClause(updateNode.getReturningClause());
339        }
340
341        this.optionClause = updateNode.getOptionClause();
342
343        errorLoggingClause = updateNode.getErrorLoggingClause();
344
345        if (updateNode.getInsertSqlNode() != null){
346            insertSqlStatement = new TInsertSqlStatement(this.dbvendor);
347            insertSqlStatement.rootNode = updateNode.getInsertSqlNode();
348            insertSqlStatement.doParseStatement(this);
349
350        }
351
352        // remove table alias A from tables like this SQL:
353        // delete  A
354        // from  myTable A
355        // join otherTable B on A.Id=B.Id
356        int deletedTables[] = new int[this.tables.size()];
357        TTable lcTable = null,lcTable2 = null;
358        for(int i=0;i<this.tables.size();i++){
359            lcTable = this.tables.getTable(i);
360            if ((lcTable.getAliasClause() == null)&&(lcTable.isBaseTable())){
361                for(int j=0;j<this.tables.size();j++){
362                    if (i == j) {continue;}
363                    lcTable2 = this.tables.getTable(j);
364                    if (lcTable2.getAliasClause() != null){
365                        if (lcTable2.getAliasClause().toString().compareToIgnoreCase(lcTable.toString()) == 0){
366                            deletedTables[i] = 1;
367                            //lcTable.setLinkTable(true);
368                            lcTable.setLinkTable(lcTable2);
369//                            for(int k=0;k<lcTable.getObjectNameReferences().size();k++){
370//                                lcTable2.getObjectNameReferences().addObjectName(lcTable.getObjectNameReferences().getObjectName(k));
371//                                //lcTable2.getLinkedColumns().addObjectName(lcTable.getObjectNameReferences().getObjectName(k));
372//                                //lcTable.getObjectNameReferences().getObjectName(k).setSourceTable(lcTable2);
373//                               // System.out.println(lcTable.getObjectNameReferences().getAliasName(k).toString());
374//                            }
375                        }
376                    }
377
378                }
379            }
380        }
381//        for(int i = this.tables.size()-1;i>=0;i--){
382//            if (deletedTables[i] == 1){
383//                //this.tables.removeElementAt(i);
384//            }
385//        }
386        
387        return 0;
388    }
389
390    public void accept(TParseTreeVisitor v){
391        v.preVisit(this);
392        v.postVisit(this);
393    }
394
395    public void acceptChildren(TParseTreeVisitor v){
396        v.preVisit(this);
397
398        if (this.getCteList() != null){
399            this.getCteList().acceptChildren(v);
400        }
401
402
403
404        if (TBaseType.USE_JOINEXPR_INSTEAD_OF_JOIN){
405
406            for(TTable table:getRelations()){
407                table.acceptChildren(v);
408            }
409
410        }else{
411            this.getTargetTable().acceptChildren(v);
412            if (this.joins.size() > 0){
413                this.joins.acceptChildren(v);
414            }
415        }
416
417        if (this.getTopClause() != null){
418            this.getTopClause().acceptChildren(v);
419        }
420
421        if (this.getOutputClause() != null){
422            this.getOutputClause().acceptChildren(v);
423        }
424
425        if (this.getResultColumnList() != null){
426            this.getResultColumnList().acceptChildren(v);
427        }else{
428            TBaseType.log("set clause in update stmt is null",TLog.WARNING);
429        }
430
431
432        if (this.getWhereClause() != null){
433            this.getWhereClause().acceptChildren(v);
434        }
435
436        if (this.getOrderByClause() != null){
437            this.getOrderByClause().acceptChildren(v);
438        }
439
440        if (this.getLimitClause() != null){
441            this.getLimitClause().acceptChildren(v);
442        }
443
444        if (this.getReturningClause() != null){
445            this.getReturningClause().acceptChildren(v);
446        }
447
448        v.postVisit(this);
449    }
450
451    public void setErrorLoggingClause(TErrorLoggingClause errorLoggingClause) {
452        this.errorLoggingClause = errorLoggingClause;
453    }
454
455    public void setOptionClause(TOptionClause optionClause) {
456        this.optionClause = optionClause;
457    }
458
459    public void setReferenceJoins(TJoinList referenceJoins) {
460        this.referenceJoins = referenceJoins;
461    }
462
463    public void setOrderByClause(TOrderBy orderByClause) {
464        this.orderByClause = orderByClause;
465    }
466
467    public void setLimitClause(TLimitClause limitClause) {
468        this.limitClause = limitClause;
469    }
470
471    private TUseKeyIndex useKeyIndex;//couchbase
472
473    public void setUseKeyIndex(TUseKeyIndex useKeyIndex) {
474        this.useKeyIndex = useKeyIndex;
475    }
476
477    public TUseKeyIndex getUseKeyIndex() {
478
479        return useKeyIndex;
480    }
481
482    private TResultColumnList unSetTerms; //couchbase
483
484    public void setUnSetTerms(TResultColumnList unSetTerms) {
485        this.unSetTerms = unSetTerms;
486    }
487
488    /**
489     * Couchbase unset clause
490     * @return Couchbase unset clause
491     */
492    public TResultColumnList getUnSetTerms() {
493
494        return unSetTerms;
495    }
496
497}