Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,14 @@ configurations.configureEach {
}

compileJavacc {
arguments = [grammar_encoding: 'UTF-8', static: 'false', java_template_type: 'modern']
arguments = [
grammar_encoding: 'UTF-8',
static: 'false',
java_template_type: 'modern',
// Comment this in to build the parser with tracing.
DEBUG_PARSER: 'false',
DEBUG_LOOKAHEAD: 'false'
]
}

java {
Expand Down
126 changes: 59 additions & 67 deletions src/main/java/net/sf/jsqlparser/expression/JsonFunction.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,47 @@
import net.sf.jsqlparser.parser.ASTNodeAccessImpl;

/**
* Represents a JSON-Function.<br>
* Currently supported are the types in {@link JsonFunctionType}.<br>
* <br>
* For JSON_OBJECT the parameters are available from {@link #getKeyValuePairs()}<br>
* <br>
* For JSON_ARRAY the parameters are availble from {@link #getExpressions()}.<br>
*
* @author <a href="mailto:[email protected]">Andreas Reichel</a>
*/

public class JsonFunction extends ASTNodeAccessImpl implements Expression {
private final ArrayList<JsonKeyValuePair> keyValuePairs = new ArrayList<>();
private final ArrayList<JsonFunctionExpression> expressions = new ArrayList<>();
private JsonFunctionType functionType;
private JsonAggregateOnNullType onNullType;
private JsonAggregateUniqueKeysType uniqueKeysType;

private boolean isStrict = false;

public JsonFunction() {}

public JsonFunction(JsonFunctionType functionType) {
this.functionType = functionType;
}

/**
* Returns the Parameters of an JSON_OBJECT<br>
* The KeyValuePairs may not have both key and value set, in some cases only the Key is set.
*
* @see net.sf.jsqlparser.parser.feature.Feature#allowCommaAsKeyValueSeparator
*
* @return A List of KeyValuePairs, never NULL
*/
public ArrayList<JsonKeyValuePair> getKeyValuePairs() {
return keyValuePairs;
}

/**
* Returns the parameters of JSON_ARRAY<br>
*
* @return A List of {@link JsonFunctionExpression}s, never NULL
*/
public ArrayList<JsonFunctionExpression> getExpressions() {
return expressions;
}
Expand Down Expand Up @@ -114,6 +141,19 @@ public JsonFunction withType(String typeName) {
return this;
}

public boolean isStrict() {
return isStrict;
}

public void setStrict(boolean strict) {
isStrict = strict;
}

public JsonFunction withStrict(boolean strict) {
this.setStrict(strict);
return this;
}

@Override
public <T, S> T accept(ExpressionVisitor<T> expressionVisitor, S context) {
return expressionVisitor.visit(this, context);
Expand All @@ -123,13 +163,9 @@ public <T, S> T accept(ExpressionVisitor<T> expressionVisitor, S context) {
public StringBuilder append(StringBuilder builder) {
switch (functionType) {
case OBJECT:
appendObject(builder);
break;
case POSTGRES_OBJECT:
appendPostgresObject(builder);
break;
case MYSQL_OBJECT:
appendMySqlObject(builder);
appendObject(builder);
break;
case ARRAY:
appendArray(builder);
Expand All @@ -148,35 +184,37 @@ public StringBuilder appendObject(StringBuilder builder) {
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");
}
keyValuePair.append(builder);
i++;
}

appendOnNullType(builder);
if (isStrict) {
builder.append(" STRICT");
}
appendUniqueKeys(builder);

builder.append(" ) ");

return builder;
}

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

private void appendUniqueKeys(StringBuilder builder) {
if (uniqueKeysType != null) {
switch (uniqueKeysType) {
case WITH:
Expand All @@ -189,41 +227,6 @@ public StringBuilder appendObject(StringBuilder builder) {
// this should never happen
}
}

builder.append(" ) ");

return builder;
}


@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"})
public StringBuilder appendPostgresObject(StringBuilder builder) {
builder.append("JSON_OBJECT( ");
for (JsonKeyValuePair keyValuePair : keyValuePairs) {
builder.append(keyValuePair.getKey());
if (keyValuePair.getValue() != null) {
builder.append(", ").append(keyValuePair.getValue());
}
}
builder.append(" ) ");

return builder;
}

public StringBuilder appendMySqlObject(StringBuilder builder) {
builder.append("JSON_OBJECT( ");
int i = 0;
for (JsonKeyValuePair keyValuePair : keyValuePairs) {
if (i > 0) {
builder.append(", ");
}
builder.append(keyValuePair.getKey());
builder.append(", ").append(keyValuePair.getValue());
i++;
}
builder.append(" ) ");

return builder;
}

@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.NPathComplexity"})
Expand All @@ -239,18 +242,7 @@ public StringBuilder appendArray(StringBuilder 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 omitted
}
}
appendOnNullType(builder);
builder.append(") ");

return builder;
Expand Down
14 changes: 13 additions & 1 deletion src/main/java/net/sf/jsqlparser/expression/JsonFunctionType.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,19 @@
* @author <a href="mailto:[email protected]">Andreas Reichel</a>
*/
public enum JsonFunctionType {
OBJECT, ARRAY, POSTGRES_OBJECT, MYSQL_OBJECT;
OBJECT, ARRAY,

/**
* Not used anymore
*/
@Deprecated
POSTGRES_OBJECT,

/**
* Not used anymore
*/
@Deprecated
MYSQL_OBJECT;

public static JsonFunctionType from(String type) {
return Enum.valueOf(JsonFunctionType.class, type.toUpperCase());
Expand Down
62 changes: 50 additions & 12 deletions src/main/java/net/sf/jsqlparser/expression/JsonKeyValuePair.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,27 @@
public class JsonKeyValuePair implements Serializable {
private final Object key;
private final Object value;
private boolean usingKeyKeyword = false;
private boolean usingValueKeyword = false;
private boolean usingKeyKeyword;
private JsonKeyValuePairSeparator separator;
private boolean usingFormatJson = false;

/**
* Please use the Constructor with {@link JsonKeyValuePairSeparator} parameter.
*/
@Deprecated
public JsonKeyValuePair(Object key, Object value, boolean usingKeyKeyword,
boolean usingValueKeyword) {
this(key, value, usingKeyKeyword, usingValueKeyword ? JsonKeyValuePairSeparator.VALUE
: JsonKeyValuePairSeparator.COLON);
}

public JsonKeyValuePair(Object key, Object value, boolean usingKeyKeyword,
JsonKeyValuePairSeparator separator) {
this.key = Objects.requireNonNull(key, "The KEY of the Pair must not be null");
this.value = value;
this.usingKeyKeyword = usingKeyKeyword;
this.usingValueKeyword = usingValueKeyword;
this.separator =
Objects.requireNonNull(separator, "The KeyValuePairSeparator must not be NULL");
}

public boolean isUsingKeyKeyword() {
Expand All @@ -45,19 +56,45 @@ public JsonKeyValuePair withUsingKeyKeyword(boolean usingKeyKeyword) {
return this;
}

/**
* Use {@link #getSeparator()}
*/
@Deprecated
public boolean isUsingValueKeyword() {
return usingValueKeyword;
return separator == JsonKeyValuePairSeparator.VALUE;
}

/**
* Use {@link #setSeparator(JsonKeyValuePairSeparator)}
*/
@Deprecated
public void setUsingValueKeyword(boolean usingValueKeyword) {
this.usingValueKeyword = usingValueKeyword;
separator = usingValueKeyword ? JsonKeyValuePairSeparator.VALUE
: JsonKeyValuePairSeparator.COLON;
}

/**
* Use {@link #withSeparator(JsonKeyValuePairSeparator)}
*/
@Deprecated
public JsonKeyValuePair withUsingValueKeyword(boolean usingValueKeyword) {
this.setUsingValueKeyword(usingValueKeyword);
return this;
}

public JsonKeyValuePairSeparator getSeparator() {
return separator;
}

public void setSeparator(JsonKeyValuePairSeparator separator) {
this.separator = separator;
}

public JsonKeyValuePair withSeparator(JsonKeyValuePairSeparator separator) {
this.setSeparator(separator);
return this;
}

public boolean isUsingFormatJson() {
return usingFormatJson;
}
Expand Down Expand Up @@ -102,13 +139,14 @@ public Object getValue() {
}

public StringBuilder append(StringBuilder builder) {
if (isUsingValueKeyword()) {
if (isUsingKeyKeyword()) {
builder.append("KEY ");
}
builder.append(getKey()).append(" VALUE ").append(getValue());
} else {
builder.append(getKey()).append(":").append(getValue());
if (isUsingKeyKeyword() && getSeparator() == JsonKeyValuePairSeparator.VALUE) {
builder.append("KEY ");
}
builder.append(getKey());

if (getValue() != null) {
builder.append(getSeparator().getSeparatorString());
builder.append(getValue());
}

if (isUsingFormatJson()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package net.sf.jsqlparser.expression;

/**
* Describes the string used to separate the key from the value.
*/
public enum JsonKeyValuePairSeparator {
VALUE(" VALUE "), COLON(":"),

// Used in MySQL dialect
COMMA(","),

// Is used in case they KeyValuePair has only a key and no value
NOT_USED("");

private final String separator;

JsonKeyValuePairSeparator(String separator) {
this.separator = separator;
}

public String getSeparatorString() {
return separator;
}
}
13 changes: 13 additions & 0 deletions src/main/java/net/sf/jsqlparser/parser/feature/Feature.java
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,19 @@ public enum Feature {
* "EXPORT"
*/
export,

/**
* MySQL allows a ',' as a separator between key and value entries. We allow that by default,
* but it can be disabled here
*/
allowCommaAsKeyValueSeparator(true),

/**
* DB2 and Oracle allow Expressions as JSON_OBJECT key values. This clashes with Informix and
* Snowflake Json-Extraction syntax
*/
allowExpressionAsJsonObjectKey(false)

;

private final Object value;
Expand Down
Loading
Loading