Skip to content

Commit 03900a2

Browse files
author
youngjoonkim
committed
feat: add postgresql named parameters support
1 parent fab8926 commit 03900a2

File tree

8 files changed

+214
-0
lines changed

8 files changed

+214
-0
lines changed

src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,4 +781,10 @@ default void visit(Inverse inverse) {
781781
<S> T visit(FromQuery fromQuery, S context);
782782

783783
<S> T visit(DateUnitExpression dateUnitExpression, S context);
784+
785+
<S> T visit(PostgresNamedFunctionParameter postgresNamedFunctionParameter, S context);
786+
787+
default void visit(PostgresNamedFunctionParameter postgresNamedFunctionParameter) {
788+
this.visit(postgresNamedFunctionParameter, null);
789+
}
784790
}

src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,11 @@ public <S> T visit(OracleNamedFunctionParameter oracleNamedFunctionParameter, S
743743
return oracleNamedFunctionParameter.getExpression().accept(this, context);
744744
}
745745

746+
@Override
747+
public <S> T visit(PostgresNamedFunctionParameter postgresNamedFunctionParameter, S context) {
748+
return postgresNamedFunctionParameter.getExpression().accept(this, context);
749+
}
750+
746751
@Override
747752
public <S> T visit(GeometryDistance geometryDistance, S context) {
748753
return visitBinaryExpression(geometryDistance, context);
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2021 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.expression;
11+
12+
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;
13+
14+
import java.util.Objects;
15+
16+
/**
17+
* @author <a href="mailto:andreas@manticore-projects.com">Andreas Reichel</a>
18+
*/
19+
public class PostgresNamedFunctionParameter extends ASTNodeAccessImpl implements Expression {
20+
private final String name;
21+
private final Expression expression;
22+
23+
public PostgresNamedFunctionParameter(String name, Expression expression) {
24+
this.name = Objects.requireNonNull(name,
25+
"The NAME of the PostgresNamedFunctionParameter must not be null.");
26+
this.expression = Objects.requireNonNull(expression,
27+
"The EXPRESSION of the PostgresNamedFunctionParameter must not be null.");
28+
}
29+
30+
public String getName() {
31+
return name;
32+
}
33+
34+
public Expression getExpression() {
35+
return expression;
36+
}
37+
38+
@Override
39+
public <T, S> T accept(ExpressionVisitor<T> expressionVisitor, S context) {
40+
return expressionVisitor.visit(this, context);
41+
}
42+
43+
public StringBuilder appendTo(StringBuilder builder) {
44+
builder.append(name)
45+
.append(" := ")
46+
.append(expression);
47+
48+
return builder;
49+
}
50+
51+
@Override
52+
public String toString() {
53+
return appendTo(new StringBuilder()).toString();
54+
}
55+
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1758,6 +1758,12 @@ public <S> Void visit(OracleNamedFunctionParameter oracleNamedFunctionParameter,
17581758
return null;
17591759
}
17601760

1761+
@Override
1762+
public <S> Void visit(PostgresNamedFunctionParameter postgresNamedFunctionParameter, S context) {
1763+
postgresNamedFunctionParameter.getExpression().accept(this, context);
1764+
return null;
1765+
}
1766+
17611767
@Override
17621768
public <S> Void visit(RenameTableStatement renameTableStatement, S context) {
17631769
for (Map.Entry<Table, Table> e : renameTableStatement.getTableNames()) {

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import net.sf.jsqlparser.expression.OracleHint;
5353
import net.sf.jsqlparser.expression.OracleNamedFunctionParameter;
5454
import net.sf.jsqlparser.expression.OverlapsCondition;
55+
import net.sf.jsqlparser.expression.PostgresNamedFunctionParameter;
5556
import net.sf.jsqlparser.expression.RangeExpression;
5657
import net.sf.jsqlparser.expression.RowConstructor;
5758
import net.sf.jsqlparser.expression.RowGetExpression;
@@ -1835,4 +1836,13 @@ public <S> StringBuilder visit(FromQuery fromQuery, S context) {
18351836
public <S> StringBuilder visit(DateUnitExpression dateUnitExpression, S context) {
18361837
return builder.append(dateUnitExpression.toString());
18371838
}
1839+
1840+
@Override
1841+
public <S> StringBuilder visit(PostgresNamedFunctionParameter postgresNamedFunctionParameter,
1842+
S context) {
1843+
builder.append(postgresNamedFunctionParameter.getName()).append(" := ");
1844+
1845+
postgresNamedFunctionParameter.getExpression().accept(this, context);
1846+
return builder;
1847+
}
18381848
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import net.sf.jsqlparser.expression.OracleHint;
5252
import net.sf.jsqlparser.expression.OracleNamedFunctionParameter;
5353
import net.sf.jsqlparser.expression.OverlapsCondition;
54+
import net.sf.jsqlparser.expression.PostgresNamedFunctionParameter;
5455
import net.sf.jsqlparser.expression.RangeExpression;
5556
import net.sf.jsqlparser.expression.RowConstructor;
5657
import net.sf.jsqlparser.expression.RowGetExpression;
@@ -1052,6 +1053,12 @@ public <S> Void visit(OracleNamedFunctionParameter oracleNamedFunctionParameter,
10521053
return null;
10531054
}
10541055

1056+
@Override
1057+
public <S> Void visit(PostgresNamedFunctionParameter postgresNamedFunctionParameter, S context) {
1058+
postgresNamedFunctionParameter.getExpression().accept(this, context);
1059+
return null;
1060+
}
1061+
10551062
@Override
10561063
public <S> Void visit(AllColumns allColumns, S context) {
10571064
return null;

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,7 @@ TOKEN: /* SQL Keywords. prefixed with K_ to avoid name clashes */
515515
| <K_OR:"OR">
516516
| <K_ORA: "ORA">
517517
| <K_ORACLE_NAMED_PARAMETER_ASSIGNMENT: "=>">
518+
| <K_POSTGRES_NAMED_PARAMETER_ASSIGNMENT: ":=">
518519
| <K_ORDER:"ORDER">
519520
| <K_ORDINALITY:"ORDINALITY">
520521
| <K_OUTER:"OUTER">
@@ -6224,6 +6225,8 @@ ExpressionList ComplexExpressionList():
62246225
(
62256226
LOOKAHEAD(2) expr=OracleNamedFunctionParameter()
62266227
|
6228+
LOOKAHEAD(2) expr=PostgresNamedFunctionParameter()
6229+
|
62276230
expr=Expression()
62286231
)
62296232
{
@@ -6235,6 +6238,8 @@ ExpressionList ComplexExpressionList():
62356238
(
62366239
LOOKAHEAD(2) expr=OracleNamedFunctionParameter()
62376240
|
6241+
LOOKAHEAD(2) expr=PostgresNamedFunctionParameter()
6242+
|
62386243
LOOKAHEAD(7) expr=LambdaExpression()
62396244
|
62406245
expr=Expression()
@@ -6786,6 +6791,20 @@ OracleNamedFunctionParameter OracleNamedFunctionParameter() : {
67866791
}
67876792
}
67886793

6794+
PostgresNamedFunctionParameter PostgresNamedFunctionParameter() : {
6795+
Token token = null;
6796+
String name = null;
6797+
Expression expression;
6798+
}
6799+
{
6800+
( name=RelObjectNameExt2() | token=<K_OUTER> )
6801+
<K_POSTGRES_NAMED_PARAMETER_ASSIGNMENT>
6802+
expression=Expression()
6803+
{
6804+
return new PostgresNamedFunctionParameter(name != null ? name : token.image, expression);
6805+
}
6806+
}
6807+
67896808
UserVariable UserVariable() : {
67906809
Token tk;
67916810
String varName;
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2021 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.expression;
11+
12+
import net.sf.jsqlparser.JSQLParserException;
13+
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
14+
import net.sf.jsqlparser.statement.Statement;
15+
import net.sf.jsqlparser.statement.StatementVisitorAdapter;
16+
import net.sf.jsqlparser.test.TestUtils;
17+
import net.sf.jsqlparser.util.TablesNamesFinder;
18+
import net.sf.jsqlparser.util.validation.ValidationTestAsserts;
19+
import net.sf.jsqlparser.util.validation.feature.DatabaseType;
20+
import net.sf.jsqlparser.util.validation.validator.ExpressionValidator;
21+
import org.junit.jupiter.api.Test;
22+
23+
import java.util.List;
24+
25+
import static org.junit.jupiter.api.Assertions.assertEquals;
26+
import static org.junit.jupiter.api.Assertions.assertTrue;
27+
28+
/**
29+
*
30+
* @author <a href="mailto:andreas@manticore-projects.com">Andreas Reichel</a>
31+
*/
32+
public class PostgresNamedFunctionParameterTest {
33+
34+
/**
35+
* This test will parse and deparse the statement and assures the functional coverage by
36+
* JSQLParser.
37+
*
38+
* @throws JSQLParserException
39+
*/
40+
@Test
41+
public void testExpression() throws JSQLParserException {
42+
String sqlStr =
43+
"select dy_api.test_func1('test_user', is_test := false) as col1";
44+
45+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
46+
47+
sqlStr = "exec dbms_stats.gather_schema_stats(\n"
48+
+ " ownname := 'COMMON', \n"
49+
+ " estimate_percent := dbms_stats.auto_sample_size, \n"
50+
+ " method_opt := 'for all columns size auto', \n"
51+
+ " degree := DBMS_STATS.DEFAULT_DEGREE,\n"
52+
+ " cascade := DBMS_STATS.AUTO_CASCADE,\n"
53+
+ " options := 'GATHER AUTO'\n"
54+
+ " )";
55+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
56+
}
57+
58+
/**
59+
* This test will trigger the method {@link ExpressionVisitorAdaptor#visit() Visit Method} in
60+
* the ExpressionVisitorAdaptor needed for the Code Coverage.
61+
*
62+
* @throws JSQLParserException
63+
*/
64+
@Test
65+
public void testExpressionVisitorAdaptor() throws JSQLParserException {
66+
String sqlStr =
67+
"select r.*, test.numeric_function ( p_1 := r.param1, p_2 := r.param2 ) as resultaat2";
68+
69+
CCJSqlParserUtil.parse(sqlStr).accept(new StatementVisitorAdapter());
70+
71+
// alternatively, for the Expression only
72+
CCJSqlParserUtil.parseExpression("p_1 := r.param1").accept(new ExpressionVisitorAdapter(),
73+
null);
74+
}
75+
76+
/**
77+
* This test will trigger the method {@link TableNamesFinder#visit() Visit Method} in the
78+
* TableNamesFinder needed for the Code Coverage.
79+
*
80+
* @throws JSQLParserException
81+
*/
82+
@Test
83+
public void testTableNamesFinder() throws JSQLParserException {
84+
String sqlStr =
85+
"select r.*, test.numeric_function ( p_1 := r.param1, p_2 := r.param2 ) as resultaat2 from test_table";
86+
87+
Statement statement = CCJSqlParserUtil.parse(sqlStr);
88+
List<String> tables = new TablesNamesFinder<>().getTableList(statement);
89+
assertEquals(1, tables.size());
90+
assertTrue(tables.contains("test_table"));
91+
}
92+
93+
/**
94+
* This test will trigger the method {@link ExpressionValidator#visit() Visit Method} in the
95+
* ExpressionValidator needed for the Code Coverage.
96+
*
97+
* @throws JSQLParserException
98+
*/
99+
@Test
100+
public void testValidator() throws JSQLParserException {
101+
String sqlStr =
102+
"select r.*, test.numeric_function ( p_1 := r.param1, p_2 := r.param2 ) as resultaat2";
103+
104+
ValidationTestAsserts.validateNoErrors(sqlStr, 1, DatabaseType.POSTGRESQL);
105+
}
106+
}

0 commit comments

Comments
 (0)