Skip to content

Commit

Permalink
Implement JSON_OBJECT and JSON_ARRAY
Browse files Browse the repository at this point in the history
Solves #1260 and dbeaver/dbeaver/#13141
  • Loading branch information
manticore-projects committed Jul 11, 2021
1 parent 85efad5 commit e3fadf6
Show file tree
Hide file tree
Showing 11 changed files with 630 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -172,4 +172,6 @@ public interface ExpressionVisitor {
void visit(TimezoneExpression aThis);

void visit(JsonAggregateFunction aThis);

void visit(JsonFunction aThis);
}
Original file line number Diff line number Diff line change
Expand Up @@ -595,4 +595,9 @@ public void visit(TimezoneExpression expr) {
public void visit(JsonAggregateFunction expression) {
expression.accept(this);
}

@Override
public void visit(JsonFunction expression) {
expression.accept(this);
}
}
230 changes: 230 additions & 0 deletions src/main/java/net/sf/jsqlparser/expression/JsonFunction.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2019 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/
package net.sf.jsqlparser.expression;

import java.util.ArrayList;
import java.util.Objects;
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;

/**
*
* @author <a href="mailto:andreas@manticore-projects.com">Andreas Reichel</a>
*/

public class JsonFunction extends ASTNodeAccessImpl implements Expression {
private JsonFunctionType functionType;
private final ArrayList<JsonKeyValuePair> keyValuePairs = new ArrayList<>();

private ArrayList<JsonFunctionExpression> expressions = new ArrayList<>();

private JsonAggregateOnNullType onNullType;
private JsonAggregateUniqueKeysType uniqueKeysType;

public JsonKeyValuePair getKeyValuePair(int i) {
return keyValuePairs.get(i);
}

public JsonFunctionExpression getExpression(int i) {
return expressions.get(i);
}

public boolean add(JsonKeyValuePair keyValuePair) {
return keyValuePairs.add(keyValuePair);
}

public void add(int i, JsonKeyValuePair keyValuePair) {
keyValuePairs.add(i, keyValuePair);
}

public boolean add(JsonFunctionExpression expression) {
return expressions.add(expression);
}

public void add(int i, JsonFunctionExpression expression) {
expressions.add(i, expression);
}

public JsonAggregateOnNullType getOnNullType() {
return onNullType;
}

public void setOnNullType(JsonAggregateOnNullType onNullType) {
this.onNullType = onNullType;
}

public JsonFunction withOnNullType(JsonAggregateOnNullType onNullType) {
this.setOnNullType(onNullType);
return this;
}

public JsonAggregateUniqueKeysType getUniqueKeysType() {
return uniqueKeysType;
}

public void setUniqueKeysType(JsonAggregateUniqueKeysType uniqueKeysType) {
this.uniqueKeysType = uniqueKeysType;
}

public JsonFunction withUniqueKeysType(JsonAggregateUniqueKeysType uniqueKeysType) {
this.setUniqueKeysType(uniqueKeysType);
return this;
}

public JsonFunctionType getType() {
return functionType;
}

public void setType(JsonFunctionType type) {
this.functionType =
Objects.requireNonNull(type, "The Type of the JSON Aggregate Function must not be null");
}

public JsonFunction withType(JsonFunctionType type) {
this.setType(type);
return this;
}

public void setType(String typeName) {
this.functionType = JsonFunctionType.valueOf(
Objects.requireNonNull(typeName, "The Type of the JSON Aggregate Function must not be null")
.toUpperCase());
}

public JsonFunction withType(String typeName) {
this.setType(typeName);
return this;
}

@Override
public void accept(ExpressionVisitor expressionVisitor) {
expressionVisitor.visit(this);
}

// avoid countless Builder --> String conversion
public StringBuilder append(StringBuilder builder) {
switch (functionType) {
case OBJECT:
appendObject(builder);
break;
case ARRAY:
appendArray(builder);
break;
default:
// this should never happen really
throw new UnsupportedOperationException("JSON Aggregate Function of the type "
+ functionType.name() + " has not been implemented yet.");
}
return builder;
}

@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"})
public StringBuilder appendObject(StringBuilder builder) {
builder.append("JSON_OBJECT( ");
int i = 0;
for (JsonKeyValuePair keyValuePair : keyValuePairs) {
if (i > 0) {
builder.append(", ");
}
if (keyValuePair.isUsingValueKeyword()) {
if (keyValuePair.isUsingKeyKeyword()) {
builder.append("KEY ");
}
builder.append(keyValuePair.getKey()).append(" VALUE ").append(keyValuePair.getValue());
} else {
builder.append(keyValuePair.getKey()).append(":").append(keyValuePair.getValue());
}

if (keyValuePair.isUsingFormatJson()) {
builder.append(" FORMAT JSON");
}
i++;
}

if (onNullType != null) {
switch (onNullType) {
case NULL:
builder.append(" NULL ON NULL");
break;
case ABSENT:
builder.append(" ABSENT On NULL");
break;
default:
// this should never happen
}
}

if (uniqueKeysType != null) {
switch (uniqueKeysType) {
case WITH:
builder.append(" WITH UNIQUE KEYS");
break;
case WITHOUT:
builder.append(" WITHOUT UNIQUE KEYS");
break;
default:
// this should never happen
}
}

builder.append(" ) ");

return builder;
}

@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"})
public StringBuilder appendArray(StringBuilder builder) {
builder.append("JSON_ARRAY( ");
int i = 0;

// @fixme: this is a workaround for NULL ON NULL parsed as expressions
if (expressions.size() == 3 && (expressions.get(0).toString().equalsIgnoreCase("null")
|| expressions.get(0).toString().equalsIgnoreCase("absent")
&& expressions.get(1).toString().equalsIgnoreCase("on")
&& expressions.get(2).toString().equalsIgnoreCase("null"))) {
for (JsonFunctionExpression expr : expressions) {
if (i > 0) {
builder.append(" ");
}
expr.append(builder);
i++;
}
} else {
for (JsonFunctionExpression expr : expressions) {
if (i > 0) {
builder.append(", ");
}
expr.append(builder);
i++;
}
}

if (onNullType != null) {
switch (onNullType) {
case NULL:
builder.append(" NULL ON NULL ");
break;
case ABSENT:
builder.append(" ABSENT On NULL ");
break;
default:
// "ON NULL" was ommitted
}
}
builder.append(") ");

return builder;
}

@Override
public String toString() {
StringBuilder builder = new StringBuilder();
return append(builder).toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*-
* #%L
* JSQLParser library
* %%
* Copyright (C) 2004 - 2021 JSQLParser
* %%
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
* #L%
*/

package net.sf.jsqlparser.expression;

import java.util.Objects;

/**
*
* @author <a href="mailto:andreas@manticore-projects.com">Andreas Reichel</a>
*/

public class JsonFunctionExpression {
private final Expression expression;

private boolean usingFormatJson = false;

public JsonFunctionExpression(Expression expression) {
this.expression = Objects.requireNonNull(expression, "The EXPRESSION must not be null");
}
public Expression getExpression() {
return expression;
}

public boolean isUsingFormatJson() {
return usingFormatJson;
}

public void setUsingFormatJson(boolean usingFormatJson) {
this.usingFormatJson = usingFormatJson;
}

public JsonFunctionExpression withUsingFormatJson(boolean usingFormatJson) {
this.setUsingFormatJson(usingFormatJson);
return this;
}

public StringBuilder append(StringBuilder builder) {
return builder.append(expression.toString()).append(usingFormatJson ? " FORMAT JSON" : "");
}

@Override
public String toString() {
return append(new StringBuilder()).toString();
}

}

0 comments on commit e3fadf6

Please sign in to comment.