001package gudusoft.gsqlparser.stmt;
002
003import gudusoft.gsqlparser.*;
004import gudusoft.gsqlparser.compiler.TFrame;
005import gudusoft.gsqlparser.compiler.TVariable;
006import gudusoft.gsqlparser.nodes.*;
007
008/**
009 * A LOOP statement executes a sequence of statements multiple times. PL/SQL provides
010 * these loop statements:
011 * <ul>
012 * <li>Basic loop</li>
013 * <li>WHILE loop</li>
014 * <li>FOR loop
015 * <p>FOR indexName IN lower_bound .. upper_bound LOOP statements END LOOP
016 * </li>
017 * <li>Cursor FOR loop</li>
018 * </ul>
019 *
020 * @see TBlockSqlStatement#bodyStatements
021 */
022
023public class TLoopStmt extends TBlockSqlStatement {
024
025    public final static int basic_loop = 1;
026    public final static int while_loop = 2;
027    public final static int for_loop = 3;
028    public final static int cursor_for_loop = 4;
029
030    private int kind = basic_loop;
031
032    public void setKind(int kind) {
033        this.kind = kind;
034        if (this.kind == cursor_for_loop){
035            this.recordName = this.indexName;
036        }
037    }
038
039    /**
040     *
041     * @return What's kind of loop statement: simple, while or for.
042
043     */
044    public int getKind() {
045        return kind;
046    }
047
048    public TLoopStmt(){
049        this(EDbVendor.dbvoracle);
050    }
051
052     public TLoopStmt(EDbVendor dbvendor){
053        super(dbvendor);
054        sqlstatementtype = ESqlStatementType.sst_loopstmt;
055        }
056
057    private TObjectNameList scalarVariables;
058
059    public TObjectNameList getScalarVariables() {
060        return scalarVariables;
061    }
062
063    /**
064     * An undeclared identifier that names the loop index.
065     * Or, it should be a record-name when kind is cursor_for_loop.
066     * @return Index name used in for-loop-statement.
067     */
068    public TObjectName getIndexName() {
069        return indexName;
070    }
071
072    private TObjectName indexName = null;
073
074    /**
075     * Oracle 26 PAIRS OF second iterand (value iterand).
076     * Example: FOR i, j IN PAIRS OF collection LOOP ... END LOOP;
077     * where i is the index/key and j is the value.
078     */
079    private TObjectName secondIndexName = null;
080
081    public void setSecondIndexName(TObjectName secondIndexName) {
082        this.secondIndexName = secondIndexName;
083    }
084
085    /**
086     * @return The second iterand name for PAIRS OF iteration, or null if not applicable.
087     */
088    public TObjectName getSecondIndexName() {
089        return secondIndexName;
090    }
091
092    /**
093     * Oracle 26 typed iterand declaration in FOR LOOP.
094     * Example: FOR i NUMBER(5,1) IN 1.0..3.0 LOOP ... END LOOP;
095     */
096    private TTypeName iterandType = null;
097
098    public void setIterandType(TTypeName iterandType) {
099        this.iterandType = iterandType;
100    }
101
102    /**
103     * @return The explicit type declaration for the iterand, or null if not specified.
104     */
105    public TTypeName getIterandType() {
106        return iterandType;
107    }
108
109    public TObjectName getRecordName() {
110        return recordName;
111    }
112
113    private  TObjectName recordName = null;
114
115    private TStatementListSqlNode stmts = null;
116
117    private TExpression lower_bound = null;
118    private TExpression upper_bound = null;
119
120    private  boolean  isReverse = false;
121
122    public void setReverse(boolean reverse) {
123        isReverse = reverse;
124    }
125
126    public boolean isReverse() {
127
128        return isReverse;
129    }
130
131    /**
132     * Used in for-loop-statement.
133     * @return
134     */
135    public TExpression getUpper_bound() {
136
137        return upper_bound;
138    }
139
140    /**
141     * Used in for-loop-statement.
142     * @return
143     */
144    public TExpression getLower_bound() {
145
146        return lower_bound;
147    }
148
149
150
151    public void setSubquery(TSelectSqlStatement subquery) {
152        this.subquery = subquery;
153    }
154
155    /**
156     *
157     * @return Select statement when {@link #kind} is cursor_for_loop.
158     */
159    public TSelectSqlStatement getSubquery() {
160        return subquery;
161    }
162
163    private TSelectSqlStatement subquery = null;
164    private TSelectSqlNode selectSqlNode = null;
165
166    public void setSelectSqlNode(TSelectSqlNode selectSqlNode) {
167        this.selectSqlNode = selectSqlNode;
168    }
169
170    private TExpressionList cursorParameterNames = null;
171
172    public void setCursorParameterNames(TExpressionList cursorParameterNames) {
173        this.cursorParameterNames = cursorParameterNames;
174    }
175
176    /**
177     *
178     * @return  Cursor parameter names  when {@link #kind} is cursor_for_loop if any.
179     */
180    public TExpressionList getCursorParameterNames() {
181
182        return cursorParameterNames;
183    }
184
185    void buildsql() {
186    }
187
188    void clear() {
189    }
190
191    String getasprettytext() {
192        return "";
193    }
194
195    void iterate(TVisitorAbs pvisitor) {
196    }
197
198    public void init(Object arg1)
199    {
200        stmts = (TStatementListSqlNode)arg1;
201    }
202
203    public void init(Object arg1,Object arg2)
204    {
205        stmts = (TStatementListSqlNode)arg1;
206        if (arg2 instanceof TExpression){
207            this.condition = (TExpression)arg2;
208        }else if (arg2 instanceof TObjectNameList){
209           this.scalarVariables = (TObjectNameList) arg2;
210           this.indexName = this.scalarVariables.getObjectName(0);
211        }else{
212            this.indexName = (TObjectName)arg2;
213        }
214    }
215
216    public void init(Object arg1,Object arg2,Object arg3)
217    {
218        init(arg1,arg2);
219        cursorName = (TObjectName)arg3;
220    }
221
222    public void init(Object arg1,Object arg2,Object arg3,Object arg4)
223    {
224        init(arg1,arg2);
225
226        lower_bound = (TExpression)arg3;
227        upper_bound = (TExpression)arg4;
228    }
229
230    /**
231     *
232     * @return Cursor name when {@link #kind} is cursor_for_loop.
233     */
234    public TObjectName getCursorName() {
235        return cursorName;
236    }
237
238    private TObjectName cursorName = null;
239    
240
241    /**
242     * Used in while-loop-statement
243     * @return If and only if the value of this expression is TRUE,
244     * the statements in while will execute.
245     */
246    public TExpression getCondition() {
247        return condition;
248    }
249
250    private TExpression condition = null;
251
252    private TExpression executeExpr;
253    private TExpressionList executeUsingVars;
254
255    public void setExecuteExpr(TExpression executeExpr) {
256        this.executeExpr = executeExpr;
257    }
258
259    public void setExecuteUsingVars(TExpressionList executeUsingVars) {
260        this.executeUsingVars = executeUsingVars;
261    }
262
263    public TExpression getExecuteExpr() {
264
265        return executeExpr;
266    }
267
268    public TExpressionList getExecuteUsingVars() {
269        return executeUsingVars;
270    }
271
272    /**
273     * Oracle 26 WHILE predicate for FOR LOOP iteration control.
274     * Example: FOR i IN 1..10 WHILE i < 5 LOOP ... END LOOP;
275     */
276    private TExpression whilePredicate;
277
278    public void setWhilePredicate(TExpression whilePredicate) {
279        this.whilePredicate = whilePredicate;
280    }
281
282    /**
283     * @return The WHILE predicate expression in FOR LOOP, or null if not specified.
284     */
285    public TExpression getWhilePredicate() {
286        return whilePredicate;
287    }
288
289    /**
290     * Oracle 26 WHEN predicate for FOR LOOP iteration control.
291     * Example: FOR i IN 1..10 WHEN MOD(i,2)=0 LOOP ... END LOOP;
292     */
293    private TExpression whenPredicate;
294
295    public void setWhenPredicate(TExpression whenPredicate) {
296        this.whenPredicate = whenPredicate;
297    }
298
299    /**
300     * @return The WHEN predicate expression in FOR LOOP, or null if not specified.
301     */
302    public TExpression getWhenPredicate() {
303        return whenPredicate;
304    }
305
306    /**
307     * Oracle 26 BY step clause for FOR LOOP iteration control.
308     * Example: FOR i IN 1..10 BY 2 LOOP ... END LOOP;
309     */
310    private TExpression stepExpression;
311
312    public void setStepExpression(TExpression stepExpression) {
313        this.stepExpression = stepExpression;
314    }
315
316    /**
317     * @return The BY step expression in FOR LOOP, or null if not specified.
318     */
319    public TExpression getStepExpression() {
320        return stepExpression;
321    }
322
323    /**
324     * Oracle 26 multiple iteration controls for FOR LOOP.
325     * Example: FOR i IN 1..3, REVERSE i+1..i+10, 51..55 LOOP ... END LOOP;
326     */
327    private TIterationControlList iterationControls;
328
329    public void setIterationControls(TIterationControlList iterationControls) {
330        this.iterationControls = iterationControls;
331    }
332
333    /**
334     * @return The list of iteration controls in FOR LOOP, or null if using legacy single iteration.
335     */
336    public TIterationControlList getIterationControls() {
337        return iterationControls;
338    }
339
340    /**
341     * @return True if this FOR LOOP has multiple iteration controls.
342     */
343    public boolean hasMultipleIterationControls() {
344        return iterationControls != null && iterationControls.size() > 1;
345    }
346
347    public int doParseStatement(TCustomSqlStatement psql) {
348        super.doParseStatement(psql);
349        this.stmtScope.setValidToDeclareVariable(true);
350
351        TFrame currentFrame = new TFrame(this.stmtScope);
352        currentFrame.pushMeToStack(getFrameStack());
353
354        switch (dbvendor){
355            case dbvmysql:
356            case dbvteradata:
357            case dbvbigquery:
358            case dbvsparksql:
359                if (rootNode == null) return -1;
360                TLoopSqlNode loopSqlNode = (TLoopSqlNode)rootNode;
361                setLabelName(loopSqlNode.getLabelName());
362
363                loopSqlNode.getStmts().doParse(this,ESqlClause.unknown);
364                for(int i=0;i<loopSqlNode.getStmts().size();i++){
365                    this.getBodyStatements().add(loopSqlNode.getStmts().getStatementSqlNode(i).getStmt());
366                }
367                break;
368            default:
369                //super.doParseStatement(psql);
370
371                if (indexName != null){
372                    this.getTopStatement().getSymbolTable().push( new TSymbolTableItem(TObjectName.ttobjParameter,this, indexName));
373                    TVariable variable = new TVariable(indexName);
374                    this.stmtScope.addSymbol(variable);
375                }
376                // Second iterand for PAIRS OF iteration (Oracle 26)
377                if (secondIndexName != null){
378                    this.getTopStatement().getSymbolTable().push( new TSymbolTableItem(TObjectName.ttobjParameter,this, secondIndexName));
379                    TVariable variable2 = new TVariable(secondIndexName);
380                    this.stmtScope.addSymbol(variable2);
381                }
382                if (this.condition != null){
383                    this.condition.doParse(this,ESqlClause.unknown);
384                }
385                if (this.whilePredicate != null){
386                    this.whilePredicate.doParse(this,ESqlClause.unknown);
387                }
388                if (this.whenPredicate != null){
389                    this.whenPredicate.doParse(this,ESqlClause.unknown);
390                }
391                if (this.stepExpression != null){
392                    this.stepExpression.doParse(this,ESqlClause.unknown);
393                }
394                // Parse iterand type if present (Oracle 26)
395                if (this.iterandType != null){
396                    this.iterandType.doParse(this,ESqlClause.unknown);
397                }
398                // Parse multiple iteration controls if present
399                if (this.iterationControls != null){
400                    for(int i=0;i<this.iterationControls.size();i++){
401                        TIterationControl ic = this.iterationControls.getIterationControl(i);
402                        if (ic.getLowerBound() != null){
403                            ic.getLowerBound().doParse(this,ESqlClause.unknown);
404                        }
405                        if (ic.getUpperBound() != null){
406                            ic.getUpperBound().doParse(this,ESqlClause.unknown);
407                        }
408                        if (ic.getStepExpression() != null){
409                            ic.getStepExpression().doParse(this,ESqlClause.unknown);
410                        }
411                        if (ic.getWhilePredicate() != null){
412                            ic.getWhilePredicate().doParse(this,ESqlClause.unknown);
413                        }
414                        if (ic.getWhenPredicate() != null){
415                            ic.getWhenPredicate().doParse(this,ESqlClause.unknown);
416                        }
417                    }
418                }
419                if (this.executeExpr != null){
420                    this.executeExpr.doParse(this,ESqlClause.unknown);
421                }
422                if (this.executeUsingVars != null){
423                    for(int i=0;i<this.executeUsingVars.size();i++){
424                        this.executeUsingVars.getExpression(i).doParse(this,ESqlClause.unknown);
425                    }
426                }
427                if (subquery != null){
428                    subquery.parsestatement(this,false);
429                }else if (selectSqlNode != null){
430                    //postgresql
431                    subquery = new TSelectSqlStatement(this.dbvendor);
432                    subquery.rootNode = selectSqlNode;
433                    subquery.doParseStatement(this);
434                }
435
436                stmts.doParse(this,ESqlClause.unknown);
437                for(int i=0;i<stmts.size();i++){
438                    this.getBodyStatements().add(stmts.getStatementSqlNode(i).getStmt());
439                }
440                if (secondIndexName != null) {
441                    this.getTopStatement().getSymbolTable().pop();
442                }
443                if (indexName != null) {
444                    this.getTopStatement().getSymbolTable().pop();
445                }
446
447                break;
448        }
449
450        currentFrame.popMeFromStack(getFrameStack());
451
452        return 0;
453    }
454
455    public void accept(TParseTreeVisitor v){
456        v.preVisit(this);
457        v.postVisit(this);
458    }
459
460    public void acceptChildren(TParseTreeVisitor v){
461        v.preVisit(this);
462        if (this.condition != null) condition.acceptChildren(v);
463        if (this.whilePredicate != null) whilePredicate.acceptChildren(v);
464        if (this.whenPredicate != null) whenPredicate.acceptChildren(v);
465        if (this.stepExpression != null) stepExpression.acceptChildren(v);
466        if (this.iterandType != null) iterandType.acceptChildren(v);
467        if (this.iterationControls != null) iterationControls.acceptChildren(v);
468        if (this.executeExpr != null) executeExpr.acceptChildren(v);
469        if (this.executeUsingVars != null) executeUsingVars.acceptChildren(v);
470        if (subquery != null) subquery.acceptChildren(v);
471        getBodyStatements().acceptChildren(v);
472        v.postVisit(this);
473    }
474
475    public void setIndexName(TObjectName indexName) {
476        this.indexName = indexName;
477    }
478
479    public void setRecordName(TObjectName recordName) {
480        this.recordName = recordName;
481    }
482
483    public void setLower_bound(TExpression lower_bound) {
484        this.lower_bound = lower_bound;
485    }
486
487    public void setUpper_bound(TExpression upper_bound) {
488        this.upper_bound = upper_bound;
489    }
490
491    public void setCursorName(TObjectName cursorName) {
492        this.cursorName = cursorName;
493    }
494
495    public void setCondition(TExpression condition) {
496        this.condition = condition;
497    }
498}