Skip to content

Commit 7c52e7f

Browse files
bigdream96youngjoonkim
andauthored
[feat] Support for the legacy Postgres named parameter (#2374)
* feat: add postgresql named parameters support * modify: PostgresNamedFunctionParameterTest.java * refactor: code formatting --------- Co-authored-by: youngjoonkim <youngjoonkim@seowoninfo.com>
1 parent 8453ca0 commit 7c52e7f

File tree

8 files changed

+206
-0
lines changed

8 files changed

+206
-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: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1761,6 +1761,13 @@ public <S> Void visit(OracleNamedFunctionParameter oracleNamedFunctionParameter,
17611761
return null;
17621762
}
17631763

1764+
@Override
1765+
public <S> Void visit(PostgresNamedFunctionParameter postgresNamedFunctionParameter,
1766+
S context) {
1767+
postgresNamedFunctionParameter.getExpression().accept(this, context);
1768+
return null;
1769+
}
1770+
17641771
@Override
17651772
public <S> Void visit(RenameTableStatement renameTableStatement, S context) {
17661773
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: 8 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,13 @@ public <S> Void visit(OracleNamedFunctionParameter oracleNamedFunctionParameter,
10521053
return null;
10531054
}
10541055

1056+
@Override
1057+
public <S> Void visit(PostgresNamedFunctionParameter postgresNamedFunctionParameter,
1058+
S context) {
1059+
postgresNamedFunctionParameter.getExpression().accept(this, context);
1060+
return null;
1061+
}
1062+
10551063
@Override
10561064
public <S> Void visit(AllColumns allColumns, S context) {
10571065
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">
@@ -6240,6 +6241,8 @@ ExpressionList ComplexExpressionList():
62406241
(
62416242
LOOKAHEAD(2) expr=OracleNamedFunctionParameter()
62426243
|
6244+
LOOKAHEAD(2) expr=PostgresNamedFunctionParameter()
6245+
|
62436246
expr=Expression()
62446247
)
62456248
{
@@ -6251,6 +6254,8 @@ ExpressionList ComplexExpressionList():
62516254
(
62526255
LOOKAHEAD(2) expr=OracleNamedFunctionParameter()
62536256
|
6257+
LOOKAHEAD(2) expr=PostgresNamedFunctionParameter()
6258+
|
62546259
LOOKAHEAD(7) expr=LambdaExpression()
62556260
|
62566261
expr=Expression()
@@ -6802,6 +6807,20 @@ OracleNamedFunctionParameter OracleNamedFunctionParameter() : {
68026807
}
68036808
}
68046809

6810+
PostgresNamedFunctionParameter PostgresNamedFunctionParameter() : {
6811+
Token token = null;
6812+
String name = null;
6813+
Expression expression;
6814+
}
6815+
{
6816+
( name=RelObjectNameExt2() | token=<K_OUTER> )
6817+
<K_POSTGRES_NAMED_PARAMETER_ASSIGNMENT>
6818+
expression=Expression()
6819+
{
6820+
return new PostgresNamedFunctionParameter(name != null ? name : token.image, expression);
6821+
}
6822+
}
6823+
68056824
UserVariable UserVariable() : {
68066825
Token tk;
68076826
String varName;
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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 concat_lower_or_upper(a := 'Hello', uppercase := true, b := 'World')";
44+
45+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
46+
}
47+
48+
/**
49+
* This test will trigger the method {@link ExpressionVisitorAdaptor#visit() Visit Method} in
50+
* the ExpressionVisitorAdaptor needed for the Code Coverage.
51+
*
52+
* @throws JSQLParserException
53+
*/
54+
@Test
55+
public void testExpressionVisitorAdaptor() throws JSQLParserException {
56+
String sqlStr =
57+
"SELECT concat_lower_or_upper(a := 'Hello', uppercase := true, b := 'World')";
58+
59+
CCJSqlParserUtil.parse(sqlStr).accept(new StatementVisitorAdapter());
60+
61+
// alternatively, for the Expression only
62+
CCJSqlParserUtil.parseExpression("a := 'Hello'").accept(new ExpressionVisitorAdapter(),
63+
null);
64+
}
65+
66+
/**
67+
* This test will trigger the method {@link TableNamesFinder#visit() Visit Method} in the
68+
* TableNamesFinder needed for the Code Coverage.
69+
*
70+
* @throws JSQLParserException
71+
*/
72+
@Test
73+
public void testTableNamesFinder() throws JSQLParserException {
74+
String sqlStr =
75+
"SELECT concat_lower_or_upper(a := 'Hello', uppercase := true, b := 'World') FROM test_table";
76+
77+
Statement statement = CCJSqlParserUtil.parse(sqlStr);
78+
List<String> tables = new TablesNamesFinder<>().getTableList(statement);
79+
assertEquals(1, tables.size());
80+
assertTrue(tables.contains("test_table"));
81+
}
82+
83+
/**
84+
* This test will trigger the method {@link ExpressionValidator#visit() Visit Method} in the
85+
* ExpressionValidator needed for the Code Coverage.
86+
*
87+
* @throws JSQLParserException
88+
*/
89+
@Test
90+
public void testValidator() throws JSQLParserException {
91+
String sqlStr =
92+
"SELECT concat_lower_or_upper(a := 'Hello', uppercase := true, b := 'World') FROM test_table";
93+
94+
ValidationTestAsserts.validateNoErrors(sqlStr, 1, DatabaseType.POSTGRESQL);
95+
}
96+
}

0 commit comments

Comments
 (0)