Skip to content

Commit 17f033d

Browse files
authored
Support Oracle anonymous blocks and implicit procedure calls (#2592)
1 parent 4be195b commit 17f033d

16 files changed

Lines changed: 738 additions & 44 deletions

File tree

src/main/java/net/sf/jsqlparser/parser/feature/Feature.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,8 @@ public enum Feature {
453453
*
454454
* @see Execute
455455
*/
456+
oracleBlock,
457+
456458
execute, executeExec, executeCall, executeExecute,
457459

458460
/**

src/main/java/net/sf/jsqlparser/statement/StatementFeatureVisitor.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
*/
1010
package net.sf.jsqlparser.statement;
1111

12+
import net.sf.jsqlparser.statement.oracle.OracleBlock;
13+
import net.sf.jsqlparser.statement.oracle.OracleAssignment;
14+
import net.sf.jsqlparser.statement.oracle.OracleNullStatement;
15+
1216
import net.sf.jsqlparser.statement.role.CreateRole;
1317
import net.sf.jsqlparser.statement.role.AlterRole;
1418
import net.sf.jsqlparser.statement.grant.Revoke;
@@ -993,4 +997,24 @@ public <S> Void visit(AlterSubscription statement, S context) {
993997
}
994998
return null;
995999
}
1000+
1001+
@Override
1002+
public <S> Void visit(OracleBlock block, S context) {
1003+
analysis.claimTopLevel();
1004+
return super.visit(block, context);
1005+
}
1006+
1007+
@Override
1008+
public <S> Void visit(OracleAssignment assignment, S context) {
1009+
analysis.claimTopLevel();
1010+
// Assignment to a local variable is not a database write.
1011+
return super.visit(assignment, context);
1012+
}
1013+
1014+
@Override
1015+
public <S> Void visit(OracleNullStatement statement, S context) {
1016+
analysis.claimTopLevel();
1017+
return null;
1018+
}
1019+
9961020
}

src/main/java/net/sf/jsqlparser/statement/StatementVisitor.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
*/
1010
package net.sf.jsqlparser.statement;
1111

12+
import net.sf.jsqlparser.statement.oracle.OracleBlock;
13+
import net.sf.jsqlparser.statement.oracle.OracleAssignment;
14+
import net.sf.jsqlparser.statement.oracle.OracleNullStatement;
15+
1216
import net.sf.jsqlparser.statement.role.CreateRole;
1317
import net.sf.jsqlparser.statement.role.AlterRole;
1418
import net.sf.jsqlparser.statement.grant.Revoke;
@@ -520,4 +524,29 @@ default <S> T visit(AlterSubscription statement, S context) {
520524
default void visit(AlterSubscription statement) {
521525
visit(statement, null);
522526
}
527+
528+
default <S> T visit(OracleBlock block, S context) {
529+
return visit((Block) block, context);
530+
}
531+
532+
default void visit(OracleBlock block) {
533+
visit(block, null);
534+
}
535+
536+
default <S> T visit(OracleAssignment assignment, S context) {
537+
return null;
538+
}
539+
540+
default void visit(OracleAssignment assignment) {
541+
visit(assignment, null);
542+
}
543+
544+
default <S> T visit(OracleNullStatement statement, S context) {
545+
return null;
546+
}
547+
548+
default void visit(OracleNullStatement statement) {
549+
visit(statement, null);
550+
}
551+
523552
}

src/main/java/net/sf/jsqlparser/statement/StatementVisitorAdapter.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
*/
1010
package net.sf.jsqlparser.statement;
1111

12+
import net.sf.jsqlparser.statement.oracle.OracleBlock;
13+
import net.sf.jsqlparser.statement.oracle.OracleAssignment;
14+
1215
import net.sf.jsqlparser.statement.role.CreateRole;
1316
import net.sf.jsqlparser.statement.role.AlterRole;
1417
import net.sf.jsqlparser.statement.role.RoleOption;
@@ -423,7 +426,7 @@ public <S> T visit(Statements statements, S context) {
423426

424427
@Override
425428
public <S> T visit(Execute execute, S context) {
426-
429+
expressionVisitor.visitExpression(execute.getExprList(), context);
427430
return null;
428431
}
429432

@@ -771,4 +774,19 @@ public <S> T visit(AlterSubscription statement, S context) {
771774
});
772775
return null;
773776
}
777+
778+
@Override
779+
public <S> T visit(OracleBlock block, S context) {
780+
block.visitChildren(expression -> expression.accept(expressionVisitor, context),
781+
statement -> statement.accept(this, context));
782+
return null;
783+
}
784+
785+
@Override
786+
public <S> T visit(OracleAssignment assignment, S context) {
787+
assignment.getTarget().accept(expressionVisitor, context);
788+
assignment.getValue().accept(expressionVisitor, context);
789+
return null;
790+
}
791+
774792
}

src/main/java/net/sf/jsqlparser/statement/execute/Execute.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,10 @@ public String toString() {
7575
}
7676

7777
public StringBuilder appendTo(StringBuilder builder, Consumer<Expression> expressionPrinter) {
78-
builder.append(execType.name()).append(' ').append(name);
78+
if (execType != ExecType.IMPLICIT) {
79+
builder.append(execType.name()).append(' ');
80+
}
81+
builder.append(name);
7982
if (exprList != null) {
8083
builder.append(' ');
8184
boolean brackets = exprList instanceof ParenthesedExpressionList;
@@ -111,7 +114,7 @@ public Execute withExprList(ExpressionList exprList) {
111114
}
112115

113116
public enum ExecType {
114-
EXECUTE, EXEC, CALL;
117+
EXECUTE, EXEC, CALL, IMPLICIT;
115118

116119
public static ExecType from(String type) {
117120
return Enum.valueOf(ExecType.class, type.toUpperCase(Locale.ROOT));
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2026 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.statement.oracle;
11+
12+
import java.util.function.Consumer;
13+
import net.sf.jsqlparser.expression.Expression;
14+
import net.sf.jsqlparser.statement.Statement;
15+
import net.sf.jsqlparser.statement.StatementVisitor;
16+
17+
/** Assignment to an Oracle block variable, record field or bind parameter. */
18+
public class OracleAssignment implements Statement {
19+
private Expression target;
20+
private Expression value;
21+
22+
public OracleAssignment(Expression target, Expression value) {
23+
this.target = target;
24+
this.value = value;
25+
}
26+
27+
public Expression getTarget() {
28+
return target;
29+
}
30+
31+
public void setTarget(Expression target) {
32+
this.target = target;
33+
}
34+
35+
public Expression getValue() {
36+
return value;
37+
}
38+
39+
public void setValue(Expression value) {
40+
this.value = value;
41+
}
42+
43+
@Override
44+
public <T, S> T accept(StatementVisitor<T> visitor, S context) {
45+
return visitor.visit(this, context);
46+
}
47+
48+
public StringBuilder appendTo(StringBuilder builder, Consumer<Expression> printer) {
49+
printer.accept(target);
50+
builder.append(" := ");
51+
printer.accept(value);
52+
return builder;
53+
}
54+
55+
@Override
56+
public String toString() {
57+
StringBuilder builder = new StringBuilder();
58+
return appendTo(builder, builder::append).toString();
59+
}
60+
}
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2026 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.statement.oracle;
11+
12+
import java.util.ArrayList;
13+
import java.util.List;
14+
import java.util.function.Consumer;
15+
import net.sf.jsqlparser.expression.Expression;
16+
import net.sf.jsqlparser.statement.Block;
17+
import net.sf.jsqlparser.statement.Statement;
18+
import net.sf.jsqlparser.statement.Statements;
19+
import net.sf.jsqlparser.statement.StatementVisitor;
20+
import net.sf.jsqlparser.statement.create.table.ColDataType;
21+
22+
/** An Oracle anonymous block with variable declarations and exception handlers. */
23+
public class OracleBlock extends Block {
24+
private final List<VariableDeclaration> declarations = new ArrayList<>();
25+
private final List<ExceptionHandler> exceptionHandlers = new ArrayList<>();
26+
27+
public List<VariableDeclaration> getDeclarations() {
28+
return declarations;
29+
}
30+
31+
public List<ExceptionHandler> getExceptionHandlers() {
32+
return exceptionHandlers;
33+
}
34+
35+
@Override
36+
public <T, S> T accept(StatementVisitor<T> visitor, S context) {
37+
return visitor.visit(this, context);
38+
}
39+
40+
public void visitChildren(Consumer<Expression> expressions, Consumer<Statement> statements) {
41+
for (VariableDeclaration declaration : declarations) {
42+
if (declaration.getInitializer() != null) {
43+
expressions.accept(declaration.getInitializer());
44+
}
45+
}
46+
if (getStatements() != null) {
47+
getStatements().forEach(statements);
48+
}
49+
exceptionHandlers.forEach(handler -> handler.getStatements().forEach(statements));
50+
}
51+
52+
public StringBuilder appendTo(StringBuilder builder, Consumer<Expression> expressions,
53+
Consumer<Statement> statements) {
54+
if (!declarations.isEmpty()) {
55+
builder.append("DECLARE\n");
56+
declarations.forEach(declaration -> {
57+
declaration.appendTo(builder, expressions);
58+
builder.append(";\n");
59+
});
60+
}
61+
builder.append("BEGIN\n");
62+
appendStatements(builder, getStatements(), statements);
63+
if (!exceptionHandlers.isEmpty()) {
64+
builder.append("EXCEPTION\n");
65+
for (ExceptionHandler handler : exceptionHandlers) {
66+
builder.append("WHEN ").append(String.join(" OR ", handler.getExceptions()))
67+
.append(" THEN\n");
68+
appendStatements(builder, handler.getStatements(), statements);
69+
}
70+
}
71+
builder.append("END");
72+
if (hasSemicolonAfterEnd()) {
73+
builder.append(';');
74+
}
75+
return builder;
76+
}
77+
78+
private static void appendStatements(StringBuilder builder, Statements body,
79+
Consumer<Statement> printer) {
80+
if (body == null) {
81+
return;
82+
}
83+
for (Statement statement : body) {
84+
printer.accept(statement);
85+
if (!(statement instanceof Block) || !((Block) statement).hasSemicolonAfterEnd()) {
86+
builder.append(';');
87+
}
88+
builder.append('\n');
89+
}
90+
}
91+
92+
@Override
93+
public StringBuilder appendTo(StringBuilder builder) {
94+
return appendTo(builder, builder::append, builder::append);
95+
}
96+
97+
public static class VariableDeclaration implements java.io.Serializable {
98+
private String name;
99+
private ColDataType dataType;
100+
private boolean constant;
101+
private boolean notNull;
102+
private String initializerOperator = ":=";
103+
private Expression initializer;
104+
105+
public String getName() {
106+
return name;
107+
}
108+
109+
public void setName(String name) {
110+
this.name = name;
111+
}
112+
113+
public ColDataType getDataType() {
114+
return dataType;
115+
}
116+
117+
public void setDataType(ColDataType type) {
118+
dataType = type;
119+
}
120+
121+
public boolean isConstant() {
122+
return constant;
123+
}
124+
125+
public void setConstant(boolean value) {
126+
constant = value;
127+
}
128+
129+
public boolean isNotNull() {
130+
return notNull;
131+
}
132+
133+
public void setNotNull(boolean value) {
134+
notNull = value;
135+
}
136+
137+
public String getInitializerOperator() {
138+
return initializerOperator;
139+
}
140+
141+
public void setInitializerOperator(String operator) {
142+
initializerOperator = operator;
143+
}
144+
145+
public Expression getInitializer() {
146+
return initializer;
147+
}
148+
149+
public void setInitializer(Expression expression) {
150+
initializer = expression;
151+
}
152+
153+
public StringBuilder appendTo(StringBuilder builder, Consumer<Expression> printer) {
154+
builder.append(name).append(' ');
155+
if (constant) {
156+
builder.append("CONSTANT ");
157+
}
158+
builder.append(dataType);
159+
if (notNull) {
160+
builder.append(" NOT NULL");
161+
}
162+
if (initializer != null) {
163+
builder.append(' ').append(initializerOperator).append(' ');
164+
printer.accept(initializer);
165+
}
166+
return builder;
167+
}
168+
169+
@Override
170+
public String toString() {
171+
StringBuilder builder = new StringBuilder();
172+
return appendTo(builder, builder::append).toString();
173+
}
174+
}
175+
public static class ExceptionHandler implements java.io.Serializable {
176+
private final List<String> exceptions = new ArrayList<>();
177+
private Statements statements;
178+
179+
public List<String> getExceptions() {
180+
return exceptions;
181+
}
182+
183+
public Statements getStatements() {
184+
return statements;
185+
}
186+
187+
public void setStatements(Statements statements) {
188+
this.statements = statements;
189+
}
190+
}
191+
}

0 commit comments

Comments
 (0)