Skip to content

Commit

Permalink
[SPARK-48358][SQL] Support for REPEAT statement
Browse files Browse the repository at this point in the history
### What changes were proposed in this pull request?
In this PR, support for REPEAT statement in SQL scripting is introduced.

Changes summary:

Grammar/parser changes
- `repeatStatement` grammar rule
- `visitRepeatStatement` rule visitor
- `RepeatStatement` logical operetor

`RepeatStatementExec` execution node
Internal sates - `Condition` and `Body`
Iterator implementation - switch between body and condition until condition evaluates to true
SqlScriptingInterpreter - added logic to transform RepeatStatement logical operator to RepeatStatementExec execution node

### Why are the changes needed?
This is a part of SQL Scripting introduced to Spark, REPEAT statement is a basic control flow construct in the SQL language.

### Does this PR introduce _any_ user-facing change?
No

### How was this patch tested?
New tests are introduced to all of the three scripting test suites: `SqlScriptingParserSuite`, `SqlScriptingExecutionNodeSuite` and `SqlScriptingInterpreterSuite`.

### Was this patch authored or co-authored using generative AI tooling?
No

Closes apache#47756 from dusantism-db/sql-scripting-repeat-statement.

Authored-by: Dušan Tišma <[email protected]>
Signed-off-by: Max Gekk <[email protected]>
  • Loading branch information
dusantism-db authored and MaxGekk committed Sep 11, 2024
1 parent 6d8b842 commit da89322
Show file tree
Hide file tree
Showing 13 changed files with 872 additions and 25 deletions.
2 changes: 2 additions & 0 deletions docs/sql-ref-ansi-compliance.md
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,7 @@ Below is a list of all the keywords in Spark SQL.
|REGEXP|non-reserved|non-reserved|not a keyword|
|RENAME|non-reserved|non-reserved|non-reserved|
|REPAIR|non-reserved|non-reserved|non-reserved|
|REPEAT|non-reserved|non-reserved|non-reserved|
|REPEATABLE|non-reserved|non-reserved|non-reserved|
|REPLACE|non-reserved|non-reserved|non-reserved|
|RESET|non-reserved|non-reserved|non-reserved|
Expand Down Expand Up @@ -734,6 +735,7 @@ Below is a list of all the keywords in Spark SQL.
|UNLOCK|non-reserved|non-reserved|non-reserved|
|UNPIVOT|non-reserved|non-reserved|non-reserved|
|UNSET|non-reserved|non-reserved|non-reserved|
|UNTIL|non-reserved|non-reserved|non-reserved|
|UPDATE|non-reserved|non-reserved|reserved|
|USE|non-reserved|non-reserved|non-reserved|
|USER|reserved|non-reserved|reserved|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ REFERENCES: 'REFERENCES';
REFRESH: 'REFRESH';
RENAME: 'RENAME';
REPAIR: 'REPAIR';
REPEAT: 'REPEAT';
REPEATABLE: 'REPEATABLE';
REPLACE: 'REPLACE';
RESET: 'RESET';
Expand Down Expand Up @@ -453,6 +454,7 @@ UNKNOWN: 'UNKNOWN';
UNLOCK: 'UNLOCK';
UNPIVOT: 'UNPIVOT';
UNSET: 'UNSET';
UNTIL: 'UNTIL';
UPDATE: 'UPDATE';
USE: 'USE';
USER: 'USER';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ compoundStatement
| beginEndCompoundBlock
| ifElseStatement
| whileStatement
| repeatStatement
| leaveStatement
| iterateStatement
;
Expand All @@ -85,6 +86,10 @@ ifElseStatement
(ELSE elseBody=compoundBody)? END IF
;

repeatStatement
: beginLabel? REPEAT compoundBody UNTIL booleanExpression END REPEAT endLabel?
;

leaveStatement
: LEAVE multipartIdentifier
;
Expand Down Expand Up @@ -1660,6 +1665,7 @@ ansiNonReserved
| REFRESH
| RENAME
| REPAIR
| REPEAT
| REPEATABLE
| REPLACE
| RESET
Expand Down Expand Up @@ -1735,6 +1741,7 @@ ansiNonReserved
| UNLOCK
| UNPIVOT
| UNSET
| UNTIL
| UPDATE
| USE
| VALUES
Expand Down Expand Up @@ -2023,6 +2030,7 @@ nonReserved
| REFRESH
| RENAME
| REPAIR
| REPEAT
| REPEATABLE
| REPLACE
| RESET
Expand Down Expand Up @@ -2107,6 +2115,7 @@ nonReserved
| UNLOCK
| UNPIVOT
| UNSET
| UNTIL
| UPDATE
| USE
| USER
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,20 @@ class AstBuilder extends DataTypeAstBuilder
WhileStatement(condition, body, Some(labelText))
}

override def visitRepeatStatement(ctx: RepeatStatementContext): RepeatStatement = {
val labelText = generateLabelText(Option(ctx.beginLabel()), Option(ctx.endLabel()))
val boolExpr = ctx.booleanExpression()

val condition = withOrigin(boolExpr) {
SingleStatement(
Project(
Seq(Alias(expression(boolExpr), "condition")()),
OneRowRelation()))}
val body = visitCompoundBody(ctx.compoundBody())

RepeatStatement(condition, body, Some(labelText))
}

private def leaveOrIterateContextHasLabel(
ctx: RuleContext, label: String, isIterate: Boolean): Boolean = {
ctx match {
Expand All @@ -275,6 +289,10 @@ class AstBuilder extends DataTypeAstBuilder
if Option(c.beginLabel()).isDefined &&
c.beginLabel().multipartIdentifier().getText.toLowerCase(Locale.ROOT).equals(label)
=> true
case c: RepeatStatementContext
if Option(c.beginLabel()).isDefined &&
c.beginLabel().multipartIdentifier().getText.toLowerCase(Locale.ROOT).equals(label)
=> true
case _ => false
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,23 @@ case class WhileStatement(
body: CompoundBody,
label: Option[String]) extends CompoundPlanStatement

/**
* Logical operator for REPEAT statement.
* @param condition Any expression evaluating to a Boolean.
* Body is executed as long as the condition evaluates to false
* @param body Compound body is a collection of statements that are executed once no matter what,
* and then as long as condition is false.
* @param label An optional label for the loop which is unique amongst all labels for statements
* within which the LOOP statement is contained.
* If an end label is specified it must match the beginning label.
* The label can be used to LEAVE or ITERATE the loop.
*/
case class RepeatStatement(
condition: SingleStatement,
body: CompoundBody,
label: Option[String]) extends CompoundPlanStatement


/**
* Logical operator for LEAVE statement.
* The statement can be used both for compounds or any kind of loops.
Expand Down
Loading

0 comments on commit da89322

Please sign in to comment.