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