Skip to content

Commit f5069d6

Browse files
committed
Support PostgreSQL DO wrappers with preserved routine body literals
1 parent a206b86 commit f5069d6

12 files changed

Lines changed: 282 additions & 1 deletion

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ public enum Feature {
457457
*/
458458
oracleBlock,
459459

460-
execute, executeExec, executeCall, executeExecute,
460+
execute, executeExec, executeCall, executeExecute, doStatement,
461461

462462
/**
463463
* SQL "EXECUTE" statement is allowed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
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;
11+
12+
import java.util.function.Consumer;
13+
import net.sf.jsqlparser.expression.StringValue;
14+
15+
/** A PostgreSQL anonymous routine. The language-specific body remains a string literal. */
16+
public class DoStatement implements Statement {
17+
private StringValue code;
18+
private String language;
19+
private boolean languageBeforeCode;
20+
21+
public StringValue getCode() {
22+
return code;
23+
}
24+
25+
public void setCode(StringValue code) {
26+
this.code = code;
27+
}
28+
29+
public DoStatement withCode(StringValue code) {
30+
setCode(code);
31+
return this;
32+
}
33+
34+
/** Returns the explicit language, or null when omitted. */
35+
public String getLanguage() {
36+
return language;
37+
}
38+
39+
public void setLanguage(String language) {
40+
this.language = language;
41+
}
42+
43+
public DoStatement withLanguage(String language) {
44+
setLanguage(language);
45+
return this;
46+
}
47+
48+
public boolean isLanguageBeforeCode() {
49+
return languageBeforeCode;
50+
}
51+
52+
public void setLanguageBeforeCode(boolean languageBeforeCode) {
53+
this.languageBeforeCode = languageBeforeCode;
54+
}
55+
56+
public StringBuilder appendTo(StringBuilder builder, Consumer<StringValue> codePrinter) {
57+
builder.append("DO ");
58+
if (languageBeforeCode && language != null) {
59+
builder.append("LANGUAGE ").append(language).append(' ');
60+
}
61+
codePrinter.accept(code);
62+
if (!languageBeforeCode && language != null) {
63+
builder.append(" LANGUAGE ").append(language);
64+
}
65+
return builder;
66+
}
67+
68+
@Override
69+
public <T, S> T accept(StatementVisitor<T> visitor, S context) {
70+
return visitor.visit(this, context);
71+
}
72+
73+
@Override
74+
public String toString() {
75+
StringBuilder builder = new StringBuilder();
76+
return appendTo(builder, value -> builder.append(value)).toString();
77+
}
78+
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,13 @@ public <S> Void visit(Execute execute, S context) {
598598
return null;
599599
}
600600

601+
@Override
602+
public <S> Void visit(DoStatement statement, S context) {
603+
analysis.claimTopLevel();
604+
analysis.opaque("DO");
605+
return null;
606+
}
607+
601608
@Override
602609
public <S> Void visit(CreateFunctionalStatement createFunctionalStatement, S context) {
603610
analysis.claimTopLevel();

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,14 @@ default void visit(Statements statements) {
210210

211211
<S> T visit(Execute execute, S context);
212212

213+
default <S> T visit(DoStatement statement, S context) {
214+
return null;
215+
}
216+
217+
default void visit(DoStatement statement) {
218+
this.visit(statement, null);
219+
}
220+
213221
default void visit(Execute execute) {
214222
this.visit(execute, null);
215223
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,12 @@ public <S> T visit(Execute execute, S context) {
423423
return null;
424424
}
425425

426+
@Override
427+
public <S> T visit(DoStatement statement, S context) {
428+
expressionVisitor.visitExpression(statement.getCode(), context);
429+
return null;
430+
}
431+
426432
@Override
427433
public <S> T visit(LockStatement lock, S context) {
428434

src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
import net.sf.jsqlparser.statement.CreateFunctionalStatement;
9292
import net.sf.jsqlparser.statement.DeclareStatement;
9393
import net.sf.jsqlparser.statement.DescribeStatement;
94+
import net.sf.jsqlparser.statement.DoStatement;
9495
import net.sf.jsqlparser.statement.ExplainStatement;
9596
import net.sf.jsqlparser.statement.IfElseStatement;
9697
import net.sf.jsqlparser.statement.OutputClause;
@@ -1564,6 +1565,12 @@ public <S> Void visit(CreateIndex createIndex, S context) {
15641565
return null;
15651566
}
15661567

1568+
@Override
1569+
public <S> Void visit(DoStatement statement, S context) {
1570+
throwUnsupported(statement);
1571+
return null;
1572+
}
1573+
15671574
@Override
15681575
public void visit(CreateIndex createIndex) {
15691576
StatementVisitor.super.visit(createIndex);

src/main/java/net/sf/jsqlparser/util/deparser/StatementDeParser.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import net.sf.jsqlparser.statement.CreateFunctionalStatement;
3939
import net.sf.jsqlparser.statement.DeclareStatement;
4040
import net.sf.jsqlparser.statement.DescribeStatement;
41+
import net.sf.jsqlparser.statement.DoStatement;
4142
import net.sf.jsqlparser.statement.ExplainStatement;
4243
import net.sf.jsqlparser.statement.IfElseStatement;
4344
import net.sf.jsqlparser.statement.PurgeStatement;
@@ -151,6 +152,11 @@ public <S> StringBuilder visit(CreateIndex createIndex, S context) {
151152
return builder;
152153
}
153154

155+
@Override
156+
public <S> StringBuilder visit(DoStatement statement, S context) {
157+
return statement.appendTo(builder, code -> code.accept(expressionDeParser, context));
158+
}
159+
154160
@Override
155161
public <S> StringBuilder visit(CreateTable createTable, S context) {
156162
CreateTableDeParser createTableDeParser = new CreateTableDeParser(this, builder);

src/main/java/net/sf/jsqlparser/util/validation/feature/PostgresqlVersion.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public enum PostgresqlVersion implements Version {
2727
EnumSet.of(// supported if used with jdbc
2828
Feature.jdbcParameter,
2929
Feature.jdbcNamedParameter, // expressions
30+
Feature.doStatement,
3031
Feature.exprLike,
3132
Feature.exprSimilarTo, // https://www.postgresql.org/docs/current/sql-select.html
3233
Feature.select,

src/main/java/net/sf/jsqlparser/util/validation/validator/StatementValidator.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import net.sf.jsqlparser.statement.Block;
3636
import net.sf.jsqlparser.statement.Commit;
3737
import net.sf.jsqlparser.statement.CreateFunctionalStatement;
38+
import net.sf.jsqlparser.statement.DoStatement;
3839
import net.sf.jsqlparser.statement.DeclareStatement;
3940
import net.sf.jsqlparser.statement.DescribeStatement;
4041
import net.sf.jsqlparser.statement.ExplainStatement;
@@ -232,6 +233,12 @@ public <S> Void visit(Execute execute, S context) {
232233
return null;
233234
}
234235

236+
@Override
237+
public <S> Void visit(DoStatement statement, S context) {
238+
validateFeature(Feature.doStatement);
239+
return null;
240+
}
241+
235242
@Override
236243
public <S> Void visit(SetStatement set, S context) {
237244
getValidator(SetStatementValidator.class).validate(set);

src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2764,6 +2764,9 @@ Statement SingleStatement() :
27642764
|
27652765
stm = Execute()
27662766
|
2767+
LOOKAHEAD({ getToken(1).kind == K_DO && Dialect.POSTGRESQL.name().equals(getAsString(Feature.dialect)) })
2768+
stm = DoStatement()
2769+
|
27672770
stm = Set()
27682771
|
27692772
stm = Reset()
@@ -2883,6 +2886,26 @@ OracleBlock.ExceptionHandler OracleExceptionHandler():
28832886
<K_THEN> body=OracleBlockStatements() { handler.setStatements(body); return handler; }
28842887
}
28852888

2889+
DoStatement DoStatement():
2890+
{
2891+
DoStatement statement = new DoStatement();
2892+
String language;
2893+
Token code;
2894+
}
2895+
{
2896+
<K_DO>
2897+
(
2898+
LOOKAHEAD({ isKeywordAhead("LANGUAGE") })
2899+
AccessKeyword("LANGUAGE") language=RelObjectName() code=<S_CHAR_LITERAL>
2900+
{ statement.setLanguage(language); statement.setLanguageBeforeCode(true); }
2901+
|
2902+
code=<S_CHAR_LITERAL>
2903+
[ LOOKAHEAD({ isKeywordAhead("LANGUAGE") }) AccessKeyword("LANGUAGE")
2904+
language=RelObjectName() { statement.setLanguage(language); } ]
2905+
)
2906+
{ statement.setCode(new StringValue(code.image)); return statement; }
2907+
}
2908+
28862909
Block Block() #Block : {
28872910
Statements stmts = new Statements();
28882911
List<Statement> list = new ArrayList<Statement>();

0 commit comments

Comments
 (0)