Skip to content

Commit 58f8fe4

Browse files
authored
Merge pull request #340 from pborissow/master
Added support for PostgreSQL JSON Functions and Operators.
2 parents 8777a8e + 2bfc9ab commit 58f8fe4

File tree

8 files changed

+118
-32
lines changed

8 files changed

+118
-32
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import net.sf.jsqlparser.expression.operators.relational.NotEqualsTo;
4747
import net.sf.jsqlparser.expression.operators.relational.RegExpMatchOperator;
4848
import net.sf.jsqlparser.expression.operators.relational.RegExpMySQLOperator;
49+
import net.sf.jsqlparser.expression.operators.relational.JsonOperator;
4950
import net.sf.jsqlparser.schema.Column;
5051
import net.sf.jsqlparser.statement.select.SubSelect;
5152

@@ -150,6 +151,8 @@ public interface ExpressionVisitor {
150151
void visit(RegExpMatchOperator rexpr);
151152

152153
void visit(JsonExpression jsonExpr);
154+
155+
void visit(JsonOperator jsonExpr);
153156

154157
void visit(RegExpMySQLOperator regExpMySQLOperator);
155158

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import net.sf.jsqlparser.expression.operators.arithmetic.*;
2525
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
2626
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
27+
import net.sf.jsqlparser.expression.operators.relational.JsonOperator;
2728
import net.sf.jsqlparser.expression.operators.relational.*;
2829
import net.sf.jsqlparser.schema.Column;
2930
import net.sf.jsqlparser.statement.select.AllColumns;
@@ -347,6 +348,11 @@ protected void visitBinaryExpression(BinaryExpression expr) {
347348
public void visit(JsonExpression jsonExpr) {
348349
visit(jsonExpr.getColumn());
349350
}
351+
352+
@Override
353+
public void visit(JsonOperator expr) {
354+
visitBinaryExpression(expr);
355+
}
350356

351357
@Override
352358
public void visit(RegExpMySQLOperator expr) {

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

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,24 +19,6 @@
1919
* <http://www.gnu.org/licenses/lgpl-2.1.html>.
2020
* #L%
2121
*/
22-
/*
23-
* Copyright (C) 2014 JSQLParser.
24-
*
25-
* This library is free software; you can redistribute it and/or
26-
* modify it under the terms of the GNU Lesser General Public
27-
* License as published by the Free Software Foundation; either
28-
* version 2.1 of the License, or (at your option) any later version.
29-
*
30-
* This library is distributed in the hope that it will be useful,
31-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
32-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
33-
* Lesser General Public License for more details.
34-
*
35-
* You should have received a copy of the GNU Lesser General Public
36-
* License along with this library; if not, write to the Free Software
37-
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
38-
* MA 02110-1301 USA
39-
*/
4022

4123
package net.sf.jsqlparser.expression;
4224

@@ -53,6 +35,7 @@ public class JsonExpression implements Expression {
5335
private Column column;
5436

5537
private List<String> idents = new ArrayList<String>();
38+
private List<String> operators = new ArrayList<String>();
5639

5740
@Override
5841
public void accept(ExpressionVisitor expressionVisitor) {
@@ -67,24 +50,33 @@ public void setColumn(Column column) {
6750
this.column = column;
6851
}
6952

70-
public List<String> getIdents() {
71-
return idents;
72-
}
73-
74-
public void setIdents(List<String> idents) {
75-
this.idents = idents;
76-
}
53+
// public List<String> getIdents() {
54+
// return idents;
55+
// }
56+
//
57+
// public void setIdents(List<String> idents) {
58+
// this.idents = idents;
59+
// operators = new ArrayList<String>();
60+
// for (String ident : idents) {
61+
// operators.add("->");
62+
// }
63+
// }
64+
//
65+
// public void addIdent(String ident) {
66+
// addIdent(ident, "->");
67+
// }
7768

78-
public void addIdent(String ident) {
69+
public void addIdent(String ident, String operator) {
7970
idents.add(ident);
71+
operators.add(operator);
8072
}
8173

8274
@Override
8375
public String toString() {
8476
StringBuilder b = new StringBuilder();
8577
b.append(column.toString());
86-
for (String ident : idents) {
87-
b.append("->").append(ident);
78+
for (int i=0; i<idents.size(); i++){
79+
b.append(operators.get(i)).append(idents.get(i));
8880
}
8981
return b.toString();
9082
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2013 JSQLParser
6+
* %%
7+
* This program is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU Lesser General Public License as
9+
* published by the Free Software Foundation, either version 2.1 of the
10+
* License, or (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Lesser Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Lesser Public
18+
* License along with this program. If not, see
19+
* <http://www.gnu.org/licenses/lgpl-2.1.html>.
20+
* #L%
21+
*/
22+
package net.sf.jsqlparser.expression.operators.relational;
23+
24+
import net.sf.jsqlparser.expression.BinaryExpression;
25+
import net.sf.jsqlparser.expression.ExpressionVisitor;
26+
27+
public class JsonOperator extends BinaryExpression {
28+
29+
private String op; //"@>"
30+
31+
public JsonOperator(String op){
32+
this.op = op;
33+
}
34+
35+
@Override
36+
public void accept(ExpressionVisitor expressionVisitor) {
37+
expressionVisitor.visit(this);
38+
}
39+
40+
@Override
41+
public String getStringExpression() {
42+
return op;
43+
}
44+
}
45+

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
import net.sf.jsqlparser.statement.execute.Execute;
5151
import net.sf.jsqlparser.statement.merge.Merge;
5252
import net.sf.jsqlparser.statement.truncate.Truncate;
53-
53+
import net.sf.jsqlparser.expression.operators.relational.JsonOperator;
5454
/**
5555
* Find all used tables within an select statement.
5656
*/
@@ -447,6 +447,10 @@ public void visit(RegExpMySQLOperator rexpr) {
447447
public void visit(JsonExpression jsonExpr) {
448448
}
449449

450+
@Override
451+
public void visit(JsonOperator jsonExpr) {
452+
}
453+
450454
@Override
451455
public void visit(AllColumns allColumns) {
452456
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
import net.sf.jsqlparser.schema.*;
3030
import net.sf.jsqlparser.statement.select.SelectVisitor;
3131
import net.sf.jsqlparser.statement.select.SubSelect;
32-
32+
import net.sf.jsqlparser.expression.operators.relational.JsonOperator;
3333
import java.util.Iterator;
3434

3535
/**
@@ -529,6 +529,11 @@ public void visit(RegExpMySQLOperator rexpr) {
529529
public void visit(JsonExpression jsonExpr) {
530530
buffer.append(jsonExpr.toString());
531531
}
532+
533+
@Override
534+
public void visit(JsonOperator jsonExpr) {
535+
visitBinaryExpression(jsonExpr, " " + jsonExpr.getStringExpression() + " ");
536+
}
532537

533538
@Override
534539
public void visit(WithinGroupExpression wgexpr) {

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,6 @@ import net.sf.jsqlparser.statement.select.*;
7474
import net.sf.jsqlparser.statement.truncate.*;
7575
import net.sf.jsqlparser.statement.update.*;
7676
import net.sf.jsqlparser.statement.merge.*;
77-
7877
import java.util.*;
7978

8079
/**
@@ -1730,6 +1729,15 @@ Expression RegularCondition():
17301729
| "~*" { result = new RegExpMatchOperator(RegExpMatchOperatorType.MATCH_CASEINSENSITIVE); }
17311730
| "!~" { result = new RegExpMatchOperator(RegExpMatchOperatorType.NOT_MATCH_CASESENSITIVE); }
17321731
| "!~*" { result = new RegExpMatchOperator(RegExpMatchOperatorType.NOT_MATCH_CASEINSENSITIVE); }
1732+
1733+
| "@>" { result = new JsonOperator("@>"); }
1734+
| "<@" { result = new JsonOperator("<@"); }
1735+
| "?" { result = new JsonOperator("?"); }
1736+
| "?|" { result = new JsonOperator("?|"); }
1737+
| "?&" { result = new JsonOperator("?&"); }
1738+
| "||" { result = new JsonOperator("||"); }
1739+
| "-" { result = new JsonOperator("-"); }
1740+
| "-#" { result = new JsonOperator("-#"); }
17331741
)
17341742

17351743
( LOOKAHEAD(2) <K_PRIOR> rightExpression=ComparisonItem() { oraclePrior = EqualsTo.ORACLE_PRIOR_END; }
@@ -2229,7 +2237,12 @@ JsonExpression JsonExpression() : {
22292237
Token token;
22302238
}
22312239
{
2232-
column=Column() ("->" token=<S_CHAR_LITERAL> {result.addIdent(token.image);} )+
2240+
column=Column() (
2241+
"->" token=<S_CHAR_LITERAL> {result.addIdent(token.image,"->");} |
2242+
"->>" token=<S_CHAR_LITERAL> {result.addIdent(token.image,"->>");} |
2243+
"#>" token=<S_CHAR_LITERAL> {result.addIdent(token.image,"#>");} |
2244+
"#>>" token=<S_CHAR_LITERAL> {result.addIdent(token.image,"#>>");}
2245+
)+
22332246
{
22342247
result.setColumn(column);
22352248
return result;

src/test/java/net/sf/jsqlparser/test/select/SelectTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1850,6 +1850,24 @@ public void testNotEqualsTo() throws JSQLParserException {
18501850

18511851
public void testJsonExpression() throws JSQLParserException {
18521852
assertSqlCanBeParsedAndDeparsed("SELECT data->'images'->'thumbnail'->'url' AS thumb FROM instagram");
1853+
assertSqlCanBeParsedAndDeparsed("SELECT * FROM sales WHERE sale->'items'->>'description' = 'milk'");
1854+
assertSqlCanBeParsedAndDeparsed("SELECT * FROM sales WHERE sale->'items'->>'quantity' = 12::TEXT");
1855+
//assertSqlCanBeParsedAndDeparsed("SELECT * FROM sales WHERE CAST(sale->'items'->>'quantity' AS integer) = 2");
1856+
assertSqlCanBeParsedAndDeparsed("SELECT SUM(CAST(sale->'items'->>'quantity' AS integer)) AS total_quantity_sold FROM sales");
1857+
assertSqlCanBeParsedAndDeparsed("SELECT sale->>'items' FROM sales");
1858+
assertSqlCanBeParsedAndDeparsed("SELECT json_typeof(sale->'items'), json_typeof(sale->'items'->'quantity') FROM sales");
1859+
1860+
1861+
//The following staments can be parsed but not deparsed
1862+
for (String statement : new String[]{
1863+
"SELECT doc->'site_name' FROM websites WHERE doc @> '{\"tags\":[{\"term\":\"paris\"}, {\"term\":\"food\"}]}'",
1864+
"SELECT * FROM sales where sale ->'items' @> '[{\"count\":0}]'",
1865+
"SELECT * FROM sales where sale ->'items' ? 'name'",
1866+
"SELECT * FROM sales where sale ->'items' -# 'name'"
1867+
}){
1868+
Select select = (Select) parserManager.parse(new StringReader(statement));
1869+
assertStatementCanBeDeparsedAs(select, statement, true);
1870+
}
18531871
}
18541872

18551873
public void testSelectInto1() throws JSQLParserException {

0 commit comments

Comments
 (0)