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 (parseStatementListLoop() != 0) return -1;
360                break;
361            case dbvoceanbase:
362                // MySQL/system tenant: the MySQL LOOP (Mantis 4813); Oracle tenant: the PL/SQL loop
363                if (isMySQLDialect()){
364                    if (parseStatementListLoop() != 0) return -1;
365                }else{
366                    parseIterationLoop();
367                }
368                break;
369            default:
370                parseIterationLoop();
371                break;
372        }
373
374        currentFrame.popMeFromStack(getFrameStack());
375
376        return 0;
377    }
378
379    /** The loop of a MySQL-family routine: a label and a statement list. */
380    private int parseStatementListLoop(){
381        if (rootNode == null) return -1;
382        TLoopSqlNode loopSqlNode = (TLoopSqlNode)rootNode;
383        setLabelName(loopSqlNode.getLabelName());
384
385        loopSqlNode.getStmts().doParse(this,ESqlClause.unknown);
386        for(int i=0;i<loopSqlNode.getStmts().size();i++){
387            this.getBodyStatements().add(loopSqlNode.getStmts().getStatementSqlNode(i).getStmt());
388        }
389        return 0;
390    }
391
392    /** The iteration loop of the other dialects (PL/SQL, PL/pgSQL, ...): index, bounds, predicates, body. */
393    private void parseIterationLoop(){
394
395                if (indexName != null){
396                    this.getTopStatement().getSymbolTable().push( new TSymbolTableItem(TObjectName.ttobjParameter,this, indexName));
397                    TVariable variable = new TVariable(indexName);
398                    this.stmtScope.addSymbol(variable);
399                }
400                // Second iterand for PAIRS OF iteration (Oracle 26)
401                if (secondIndexName != null){
402                    this.getTopStatement().getSymbolTable().push( new TSymbolTableItem(TObjectName.ttobjParameter,this, secondIndexName));
403                    TVariable variable2 = new TVariable(secondIndexName);
404                    this.stmtScope.addSymbol(variable2);
405                }
406                if (this.condition != null){
407                    this.condition.doParse(this,ESqlClause.unknown);
408                }
409                if (this.whilePredicate != null){
410                    this.whilePredicate.doParse(this,ESqlClause.unknown);
411                }
412                if (this.whenPredicate != null){
413                    this.whenPredicate.doParse(this,ESqlClause.unknown);
414                }
415                if (this.stepExpression != null){
416                    this.stepExpression.doParse(this,ESqlClause.unknown);
417                }
418                // Parse iterand type if present (Oracle 26)
419                if (this.iterandType != null){
420                    this.iterandType.doParse(this,ESqlClause.unknown);
421                }
422                // Parse multiple iteration controls if present
423                if (this.iterationControls != null){
424                    for(int i=0;i<this.iterationControls.size();i++){
425                        TIterationControl ic = this.iterationControls.getIterationControl(i);
426                        if (ic.getLowerBound() != null){
427                            ic.getLowerBound().doParse(this,ESqlClause.unknown);
428                        }
429                        if (ic.getUpperBound() != null){
430                            ic.getUpperBound().doParse(this,ESqlClause.unknown);
431                        }
432                        if (ic.getStepExpression() != null){
433                            ic.getStepExpression().doParse(this,ESqlClause.unknown);
434                        }
435                        if (ic.getWhilePredicate() != null){
436                            ic.getWhilePredicate().doParse(this,ESqlClause.unknown);
437                        }
438                        if (ic.getWhenPredicate() != null){
439                            ic.getWhenPredicate().doParse(this,ESqlClause.unknown);
440                        }
441                    }
442                }
443                if (this.executeExpr != null){
444                    this.executeExpr.doParse(this,ESqlClause.unknown);
445                }
446                if (this.executeUsingVars != null){
447                    for(int i=0;i<this.executeUsingVars.size();i++){
448                        this.executeUsingVars.getExpression(i).doParse(this,ESqlClause.unknown);
449                    }
450                }
451                if (subquery != null){
452                    subquery.parsestatement(this,false);
453                }else if (selectSqlNode != null){
454                    //postgresql
455                    subquery = new TSelectSqlStatement(this.dbvendor);
456                    subquery.rootNode = selectSqlNode;
457                    subquery.doParseStatement(this);
458                }
459
460                stmts.doParse(this,ESqlClause.unknown);
461                for(int i=0;i<stmts.size();i++){
462                    this.getBodyStatements().add(stmts.getStatementSqlNode(i).getStmt());
463                }
464                if (secondIndexName != null) {
465                    this.getTopStatement().getSymbolTable().pop();
466                }
467                if (indexName != null) {
468                    this.getTopStatement().getSymbolTable().pop();
469                }
470
471    }
472
473    public void accept(TParseTreeVisitor v){
474        v.preVisit(this);
475        v.postVisit(this);
476    }
477
478    public void acceptChildren(TParseTreeVisitor v){
479        v.preVisit(this);
480        if (this.condition != null) condition.acceptChildren(v);
481        if (this.whilePredicate != null) whilePredicate.acceptChildren(v);
482        if (this.whenPredicate != null) whenPredicate.acceptChildren(v);
483        if (this.stepExpression != null) stepExpression.acceptChildren(v);
484        if (this.iterandType != null) iterandType.acceptChildren(v);
485        if (this.iterationControls != null) iterationControls.acceptChildren(v);
486        if (this.executeExpr != null) executeExpr.acceptChildren(v);
487        if (this.executeUsingVars != null) executeUsingVars.acceptChildren(v);
488        if (subquery != null) subquery.acceptChildren(v);
489        getBodyStatements().acceptChildren(v);
490        v.postVisit(this);
491    }
492
493    public void setIndexName(TObjectName indexName) {
494        this.indexName = indexName;
495    }
496
497    public void setRecordName(TObjectName recordName) {
498        this.recordName = recordName;
499    }
500
501    public void setLower_bound(TExpression lower_bound) {
502        this.lower_bound = lower_bound;
503    }
504
505    public void setUpper_bound(TExpression upper_bound) {
506        this.upper_bound = upper_bound;
507    }
508
509    public void setCursorName(TObjectName cursorName) {
510        this.cursorName = cursorName;
511    }
512
513    public void setCondition(TExpression condition) {
514        this.condition = condition;
515    }
516}