Skip to content

Commit 55b2363

Browse files
authored
Separate JSON operators with spaces so #> deparses correctly (#2487)
JsonExpression.toString glued each operator directly onto its operand, so a #> ... deparsed as a#>... . Because # is a legal identifier character, that re-parses as a# > ... -- a GreaterThan, a different tree from the original JsonExpression. Emit a space on each side of the operator, matching the JsonOperator visitor.
1 parent 7013909 commit 55b2363

2 files changed

Lines changed: 20 additions & 1 deletion

File tree

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,10 @@ public String toString() {
8787
StringBuilder b = new StringBuilder();
8888
b.append(expr.toString());
8989
for (Map.Entry<Expression, String> ident : idents) {
90-
b.append(ident.getValue()).append(ident.getKey());
90+
// Separate the operator with spaces: without them `#>`/`#>>` glue onto
91+
// the operand (e.g. `a#>'{b}'`), and since `#` is a legal identifier
92+
// character the result re-parses as `a#` `>` `...` -- a different tree.
93+
b.append(' ').append(ident.getValue()).append(' ').append(ident.getKey());
9194
}
9295
return b.toString();
9396
}

src/test/java/net/sf/jsqlparser/expression/JsonExpressionTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
package net.sf.jsqlparser.expression;
1111

1212
import net.sf.jsqlparser.JSQLParserException;
13+
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
1314
import net.sf.jsqlparser.statement.select.PlainSelect;
1415
import org.junit.jupiter.api.Assertions;
1516
import org.junit.jupiter.api.Test;
@@ -39,6 +40,21 @@ void testIssue1792() throws JSQLParserException {
3940
assertSqlCanBeParsedAndDeparsed(sqlStr, true);
4041
}
4142

43+
@Test
44+
void testHashArrowOperatorsRoundTrip() throws JSQLParserException {
45+
// #> and #>> must survive deparse+reparse: without spaces the operator glues
46+
// onto the object (a#>'{b}') and re-parses as the comparison a# > '{b}'.
47+
for (String sqlStr : new String[] {"SELECT a #> '{b}' FROM t",
48+
"SELECT a #>> '{b}' FROM t"}) {
49+
PlainSelect st = (PlainSelect) CCJSqlParserUtil.parse(sqlStr);
50+
Assertions.assertInstanceOf(JsonExpression.class, st.getSelectItem(0).getExpression());
51+
PlainSelect reparsed = (PlainSelect) CCJSqlParserUtil.parse(st.toString());
52+
Assertions.assertInstanceOf(JsonExpression.class,
53+
reparsed.getSelectItem(0).getExpression(),
54+
sqlStr + " deparsed to " + st);
55+
}
56+
}
57+
4258
@Test
4359
void testSnowflakeGetOperator() throws JSQLParserException {
4460
// https://docs.snowflake.com/en/user-guide/querying-semistructured

0 commit comments

Comments
 (0)