This is an automated email from the ASF dual-hosted git repository.
hongze pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/master by this push:
new 02eb106 [CALCITE-3010] In SQL parser, move JsonValueExpression into
Expression
02eb106 is described below
commit 02eb106bcd1b0fdb1d0be06d117a94eddc9f3fce
Author: Hongze Zhang <[email protected]>
AuthorDate: Fri Apr 19 23:40:12 2019 +0800
[CALCITE-3010] In SQL parser, move JsonValueExpression into Expression
* SqlJsonValueExpressionOperator is now a postfix operator;
* Remove SqlStdOperatorTable.JSON_STRUCTURED_VALUE_EXPRESSION;
* Remove SqlStdOperatorTable.JSON_API_COMMON_SYNTAX_WITHOUT_PATH;
* When "FORMAT JSON" is implicit, a call to SqlJsonValueExpressionOperator
is added and validated during sql-to-rel process;
* Correct JSON_DEPTH's return type.
---
core/src/main/codegen/templates/Parser.jj | 145 +++------------------
.../calcite/adapter/enumerable/RexImpTable.java | 6 -
.../org/apache/calcite/runtime/SqlFunctions.java | 34 ++---
.../sql/fun/SqlJsonApiCommonSyntaxOperator.java | 30 +----
.../sql/fun/SqlJsonArrayAggAggFunction.java | 7 +-
.../calcite/sql/fun/SqlJsonArrayFunction.java | 5 +-
.../calcite/sql/fun/SqlJsonDepthFunction.java | 4 +-
.../calcite/sql/fun/SqlJsonKeysFunction.java | 20 +--
.../calcite/sql/fun/SqlJsonLengthFunction.java | 21 +--
.../sql/fun/SqlJsonObjectAggAggFunction.java | 12 +-
.../calcite/sql/fun/SqlJsonObjectFunction.java | 16 ++-
.../sql/fun/SqlJsonValueExpressionOperator.java | 39 ++----
.../calcite/sql/fun/SqlStdOperatorTable.java | 15 +--
.../org/apache/calcite/sql/type/InferTypes.java | 14 ++
.../calcite/sql2rel/StandardConvertletTable.java | 80 +++++++++++-
.../src/main/java/org/apache/calcite/util/Bug.java | 5 +
.../org/apache/calcite/util/BuiltInMethod.java | 4 -
.../calcite/rel/rel2sql/RelToSqlConverterTest.java | 13 ++
.../apache/calcite/sql/parser/SqlParserTest.java | 97 ++++++++------
.../apache/calcite/sql/test/SqlAdvisorTest.java | 1 +
.../calcite/sql/test/SqlOperatorBaseTest.java | 42 +++---
.../apache/calcite/test/SqlJsonFunctionsTest.java | 15 +--
.../apache/calcite/test/SqlToRelConverterTest.java | 40 ++++++
.../org/apache/calcite/test/SqlValidatorTest.java | 29 ++++-
.../apache/calcite/test/SqlToRelConverterTest.xml | 101 ++++++++++++--
core/src/test/resources/sql/agg.iq | 123 +++++++++--------
26 files changed, 493 insertions(+), 425 deletions(-)
diff --git a/core/src/main/codegen/templates/Parser.jj
b/core/src/main/codegen/templates/Parser.jj
index 77ecacb..cd49717 100644
--- a/core/src/main/codegen/templates/Parser.jj
+++ b/core/src/main/codegen/templates/Parser.jj
@@ -3090,6 +3090,7 @@ List<Object> Expression2(ExprContext exprContext) :
{
Expression2b(exprContext, list)
(
+ LOOKAHEAD(2)
(
LOOKAHEAD(2)
(
@@ -4882,20 +4883,10 @@ SqlNode BuiltinFunctionCall() :
|
node = JsonValueFunctionCall() { return node; }
|
- node = JsonKeysFunctionCall() { return node; }
- |
- node = JsonPrettyFunctionCall() { return node; }
- |
node = JsonQueryFunctionCall() { return node; }
|
node = JsonObjectFunctionCall() { return node; }
|
- node = JsonTypeFunctionCall() { return node; }
- |
- node = JsonDepthFunctionCall() { return node; }
- |
- node = JsonLengthFunctionCall() { return node; }
- |
node = JsonObjectAggFunctionCall() { return node; }
|
node = JsonArrayFunctionCall() { return node; }
@@ -4912,7 +4903,7 @@ SqlJsonEncoding JsonRepresentation() :
<JSON>
[
// Encoding is currently ignored.
- <ENCODING>
+ LOOKAHEAD(2) <ENCODING>
(
<UTF8> { return SqlJsonEncoding.UTF8; }
|
@@ -4956,30 +4947,6 @@ SqlDataTypeSpec JsonOutputClause() :
}
}
-SqlNode JsonValueExpression(boolean implicitFormatJson) :
-{
- SqlNode e;
- List<SqlNode> args = new ArrayList<SqlNode>();
- Span span;
-}
-{
- e = Expression(ExprContext.ACCEPT_NON_QUERY) {
- args.add(e);
- span = Span.of(e);
- }
- [
- JsonInputClause() {
- return
SqlStdOperatorTable.JSON_VALUE_EXPRESSION.createCall(span.end(this), args);
- }
- ]
- {
- if (implicitFormatJson) {
- return
SqlStdOperatorTable.JSON_VALUE_EXPRESSION.createCall(span.end(this), args);
- }
- return
SqlStdOperatorTable.JSON_STRUCTURED_VALUE_EXPRESSION.createCall(span.end(this),
args);
- }
-}
-
SqlNode JsonPathSpec() :
{
SqlNode e;
@@ -4995,17 +4962,15 @@ SqlNode JsonApiCommonSyntax(boolean acceptNonPath) :
SqlNode e;
List<SqlNode> args = new ArrayList<SqlNode>();
Span span;
- SqlOperator op;
}
{
- e = JsonValueExpression(true) {
+ e = Expression(ExprContext.ACCEPT_NON_QUERY) {
args.add(e);
span = Span.of(e);
}
(
<COMMA>
e = Expression(ExprContext.ACCEPT_NON_QUERY) {
- op = SqlStdOperatorTable.JSON_API_COMMON_SYNTAX;
args.add(e);
}
|
@@ -5013,11 +4978,10 @@ SqlNode JsonApiCommonSyntax(boolean acceptNonPath) :
if (!acceptNonPath) {
throw new
ParseException(RESOURCE.jsonPathMustBeSpecified().str());
}
- op = SqlStdOperatorTable.JSON_API_COMMON_SYNTAX_WITHOUT_PATH;
}
)
[
- <PASSING> e = JsonValueExpression(false) {
+ <PASSING> e = Expression(ExprContext.ACCEPT_NON_QUERY) {
args.add(e);
}
<AS> e = SimpleIdentifier() {
@@ -5025,7 +4989,7 @@ SqlNode JsonApiCommonSyntax(boolean acceptNonPath) :
}
(
<COMMA>
- e = JsonValueExpression(false) {
+ e = Expression(ExprContext.ACCEPT_NON_QUERY) {
args.add(e);
}
<AS> e = SimpleIdentifier() {
@@ -5034,7 +4998,7 @@ SqlNode JsonApiCommonSyntax(boolean acceptNonPath) :
)*
]
{
- return op.createCall(span.end(this), args);
+ return
SqlStdOperatorTable.JSON_API_COMMON_SYNTAX.createCall(span.end(this), args);
}
}
@@ -5147,39 +5111,6 @@ SqlCall JsonValueFunctionCall() :
}
}
-SqlCall JsonKeysFunctionCall() :
-{
- final SqlNode[] args = new SqlNode[1];
- SqlNode e;
- final Span span;
- List<SqlNode> behavior;
-}
-{
- <JSON_KEYS> { span = span(); }
- <LPAREN> e = JsonApiCommonSyntax(true) {
- args[0] = e;
- }
- <RPAREN> {
- return SqlStdOperatorTable.JSON_KEYS.createCall(span.end(this), args);
- }
-}
-
-SqlCall JsonPrettyFunctionCall() :
-{
- final SqlNode[] args = new SqlNode[1];
- SqlNode e;
- final Span span;
-}
-{
- <JSON_PRETTY> { span = span(); }
- <LPAREN> e = JsonValueExpression(true) {
- args[0] = e;
- }
- <RPAREN> {
- return SqlStdOperatorTable.JSON_PRETTY.createCall(span.end(this),
args);
- }
-}
-
List<SqlNode> JsonQueryEmptyOrErrorBehavior() :
{
final List<SqlNode> list = new ArrayList<SqlNode>();
@@ -5305,7 +5236,7 @@ List<SqlNode> JsonNameAndValue() :
}
}
)
- e = JsonValueExpression(false) {
+ e = Expression(ExprContext.ACCEPT_NON_QUERY) {
list.add(e);
}
{
@@ -5361,55 +5292,6 @@ SqlCall JsonObjectFunctionCall() :
}
}
-SqlCall JsonTypeFunctionCall() :
-{
- final SqlNode[] args = new SqlNode[1];
- SqlNode e;
- final Span span;
-}
-{
- <JSON_TYPE> { span = span(); }
- <LPAREN> e = JsonValueExpression(true) {
- args[0] = e;
- }
- <RPAREN> {
- return SqlStdOperatorTable.JSON_TYPE.createCall(span.end(this), args);
- }
-}
-
-SqlCall JsonDepthFunctionCall() :
-{
- final SqlNode[] args = new SqlNode[1];
- SqlNode e;
- final Span span;
-}
-{
- <JSON_DEPTH> { span = span(); }
- <LPAREN> e = JsonValueExpression(true) {
- args[0] = e;
- }
- <RPAREN> {
- return SqlStdOperatorTable.JSON_DEPTH.createCall(span.end(this), args);
- }
-}
-
-SqlCall JsonLengthFunctionCall() :
-{
- final SqlNode[] args = new SqlNode[1];
- SqlNode e;
- final Span span;
- List<SqlNode> behavior;
-}
-{
- <JSON_LENGTH> { span = span(); }
- <LPAREN> e = JsonApiCommonSyntax(true) {
- args[0] = e;
- }
- <RPAREN> {
- return SqlStdOperatorTable.JSON_LENGTH.createCall(span.end(this),
args);
- }
-}
-
SqlCall JsonObjectAggFunctionCall() :
{
final SqlNode[] args = new SqlNode[2];
@@ -5447,12 +5329,12 @@ SqlCall JsonArrayFunctionCall() :
<JSON_ARRAY> { span = span(); }
<LPAREN> [
LOOKAHEAD(2)
- e = JsonValueExpression(false) {
+ e = Expression(ExprContext.ACCEPT_NON_QUERY) {
elements.add(e);
}
(
<COMMA>
- e = JsonValueExpression(false) {
+ e = Expression(ExprContext.ACCEPT_NON_QUERY) {
elements.add(e);
}
)*
@@ -5498,7 +5380,7 @@ SqlCall JsonArrayAggFunctionCall() :
}
{
<JSON_ARRAYAGG> { span = span(); }
- <LPAREN> e = JsonValueExpression(false) {
+ <LPAREN> e = Expression(ExprContext.ACCEPT_NON_QUERY) {
valueExpr = e;
}
orderList = JsonArrayAggOrderByClause()
@@ -6232,6 +6114,13 @@ SqlPostfixOperator PostfixRowOperator() :
| <JSON> { return SqlStdOperatorTable.IS_JSON_VALUE; }
)
)
+|
+ <FORMAT>
+ (
+ JsonRepresentation() {
+ return SqlStdOperatorTable.JSON_VALUE_EXPRESSION;
+ }
+ )
}
diff --git
a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index 157e10d..87afe4b 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -162,7 +162,6 @@ import static
org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_NULL;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.IS_TRUE;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.ITEM;
import static
org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_API_COMMON_SYNTAX;
-import static
org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_API_COMMON_SYNTAX_WITHOUT_PATH;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_ARRAY;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_ARRAYAGG;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_DEPTH;
@@ -173,7 +172,6 @@ import static
org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_OBJECT;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_OBJECTAGG;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_PRETTY;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_QUERY;
-import static
org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_STRUCTURED_VALUE_EXPRESSION;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_TYPE;
import static org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_VALUE_ANY;
import static
org.apache.calcite.sql.fun.SqlStdOperatorTable.JSON_VALUE_EXPRESSION;
@@ -457,12 +455,8 @@ public class RexImpTable {
// Json Operators
defineMethod(JSON_VALUE_EXPRESSION,
BuiltInMethod.JSON_VALUE_EXPRESSION.method, NullPolicy.STRICT);
- defineMethod(JSON_STRUCTURED_VALUE_EXPRESSION,
- BuiltInMethod.JSON_STRUCTURED_VALUE_EXPRESSION.method,
NullPolicy.STRICT);
defineMethod(JSON_API_COMMON_SYNTAX,
BuiltInMethod.JSON_API_COMMON_SYNTAX.method,
NullPolicy.NONE);
- defineMethod(JSON_API_COMMON_SYNTAX_WITHOUT_PATH,
- BuiltInMethod.JSON_API_COMMON_SYNTAX_WITHOUT_PATH.method,
NullPolicy.NONE);
defineMethod(JSON_EXISTS, BuiltInMethod.JSON_EXISTS.method,
NullPolicy.NONE);
defineMethod(JSON_VALUE_ANY, BuiltInMethod.JSON_VALUE_ANY.method,
NullPolicy.NONE);
defineMethod(JSON_QUERY, BuiltInMethod.JSON_QUERY.method, NullPolicy.NONE);
diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
index 2298661..aa3d7d4 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -2456,10 +2456,6 @@ public class SqlFunctions {
}
}
- public static Object jsonStructuredValueExpression(Object input) {
- return input;
- }
-
public static PathContext jsonApiCommonSyntax(Object input) {
return jsonApiCommonSyntax(input, "strict $");
}
@@ -2807,14 +2803,17 @@ public class SqlFunctions {
}
public static Integer jsonLength(Object input) {
+ return jsonLength(jsonApiCommonSyntax(input));
+ }
+
+ public static Integer jsonLength(Object input, String pathSpec) {
+ return jsonLength(jsonApiCommonSyntax(input, pathSpec));
+ }
+
+ public static Integer jsonLength(PathContext context) {
final Integer result;
final Object value;
try {
- if (!isJsonPathContext(input)) {
- throw RESOURCE.invalidInputForJsonLength(
- input.toString()).ex();
- }
- PathContext context = (PathContext) input;
if (context.exc != null) {
throw toUnchecked(context.exc);
}
@@ -2835,20 +2834,23 @@ public class SqlFunctions {
}
} catch (Exception ex) {
throw RESOURCE.invalidInputForJsonLength(
- input.toString()).ex();
+ context.toString()).ex();
}
return result;
}
public static String jsonKeys(Object input) {
+ return jsonKeys(jsonApiCommonSyntax(input));
+ }
+
+ public static String jsonKeys(Object input, String pathSpec) {
+ return jsonKeys(jsonApiCommonSyntax(input, pathSpec));
+ }
+
+ public static String jsonKeys(PathContext context) {
List<String> list = new ArrayList<>();
final Object value;
try {
- if (!isJsonPathContext(input)) {
- throw RESOURCE.invalidInputForJsonLength(
- input.toString()).ex();
- }
- PathContext context = (PathContext) input;
if (context.exc != null) {
throw toUnchecked(context.exc);
}
@@ -2864,7 +2866,7 @@ public class SqlFunctions {
}
} catch (Exception ex) {
throw RESOURCE.invalidInputForJsonKeys(
- input.toString()).ex();
+ context.toString()).ex();
}
return jsonize(list);
}
diff --git
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonApiCommonSyntaxOperator.java
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonApiCommonSyntaxOperator.java
index 6557935..7fdfe43 100644
---
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonApiCommonSyntaxOperator.java
+++
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonApiCommonSyntaxOperator.java
@@ -22,10 +22,8 @@ import org.apache.calcite.sql.SqlSpecialOperator;
import org.apache.calcite.sql.SqlWriter;
import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
-import org.apache.calcite.sql.type.SqlOperandTypeChecker;
import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeName;
-import org.apache.calcite.sql.validate.SqlValidator;
/**
* The JSON API common syntax including a path specification, which is for
@@ -33,38 +31,20 @@ import org.apache.calcite.sql.validate.SqlValidator;
*/
public class SqlJsonApiCommonSyntaxOperator extends SqlSpecialOperator {
- // If true, the syntax must contain a JSON path expression, e.g.
'{'foo':'bar'}', 'lax $.foo';
- // otherwise the syntax can be specified within JSON text only, e.g.
'{'foo':'bar'}'.
- private final boolean hasPath;
-
- public SqlJsonApiCommonSyntaxOperator(String name, boolean hasPath) {
+ public SqlJsonApiCommonSyntaxOperator(String name) {
super(name, SqlKind.JSON_API_COMMON_SYNTAX, 100, true,
ReturnTypes.explicit(SqlTypeName.ANY), null,
- hasPath ? OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.STRING)
- : OperandTypes.family(SqlTypeFamily.ANY));
- this.hasPath = hasPath;
- }
-
- @Override protected void checkOperandCount(SqlValidator validator,
- SqlOperandTypeChecker argType, SqlCall call) {
- if (hasPath) {
- if (call.operandCount() < 2) {
- throw new UnsupportedOperationException(
- "JSON API common syntax requires at least 2 parameters");
- }
- } else {
- if (call.operandCount() < 1) {
- throw new UnsupportedOperationException(
- "JSON API common syntax requires at least 1 parameter");
- }
- }
+ OperandTypes.or(OperandTypes.family(SqlTypeFamily.ANY,
SqlTypeFamily.STRING),
+ OperandTypes.family(SqlTypeFamily.ANY)));
}
@Override public void unparse(SqlWriter writer, SqlCall call, int leftPrec,
int rightPrec) {
+ final boolean hasPath = call.operandCount() % 2 == 0;
SqlWriter.Frame frame = writer.startList(SqlWriter.FrameTypeEnum.SIMPLE);
call.operand(0).unparse(writer, 0, 0);
if (hasPath) {
+ // the case '{'foo':'bar'}', 'lax $.foo'
writer.sep(",", true);
call.operand(1).unparse(writer, 0, 0);
}
diff --git
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayAggAggFunction.java
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayAggAggFunction.java
index 6fcbc3f..94d8670 100644
---
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayAggAggFunction.java
+++
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayAggAggFunction.java
@@ -26,6 +26,7 @@ import org.apache.calcite.sql.SqlLiteral;
import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlWriter;
import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.type.InferTypes;
import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
import org.apache.calcite.sql.type.SqlTypeFamily;
@@ -44,9 +45,9 @@ public class SqlJsonArrayAggAggFunction extends
SqlAggFunction {
public SqlJsonArrayAggAggFunction(SqlKind kind,
SqlJsonConstructorNullClause nullClause) {
- super(kind + "_" + nullClause.name(), null, kind,
ReturnTypes.VARCHAR_2000, null,
- OperandTypes.family(SqlTypeFamily.ANY), SqlFunctionCategory.SYSTEM,
- false, false, Optionality.OPTIONAL);
+ super(kind + "_" + nullClause.name(), null, kind, ReturnTypes.VARCHAR_2000,
+ InferTypes.ANY_NULLABLE, OperandTypes.family(SqlTypeFamily.ANY),
+ SqlFunctionCategory.SYSTEM, false, false, Optionality.OPTIONAL);
this.nullClause = Objects.requireNonNull(nullClause);
}
diff --git
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayFunction.java
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayFunction.java
index 29ae0f8..f13b01b 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonArrayFunction.java
@@ -26,6 +26,7 @@ import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlOperandCountRange;
import org.apache.calcite.sql.SqlWriter;
import org.apache.calcite.sql.parser.SqlParserPos;
+import org.apache.calcite.sql.type.InferTypes;
import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
import org.apache.calcite.sql.type.SqlOperandCountRanges;
@@ -39,8 +40,8 @@ import java.util.Locale;
*/
public class SqlJsonArrayFunction extends SqlFunction {
public SqlJsonArrayFunction() {
- super("JSON_ARRAY", SqlKind.OTHER_FUNCTION, ReturnTypes.VARCHAR_2000, null,
- OperandTypes.VARIADIC, SqlFunctionCategory.SYSTEM);
+ super("JSON_ARRAY", SqlKind.OTHER_FUNCTION, ReturnTypes.VARCHAR_2000,
+ InferTypes.ANY_NULLABLE, OperandTypes.VARIADIC,
SqlFunctionCategory.SYSTEM);
}
@Override public SqlOperandCountRange getOperandCountRange() {
diff --git
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonDepthFunction.java
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonDepthFunction.java
index 6137096..81cc322 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonDepthFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonDepthFunction.java
@@ -28,6 +28,7 @@ import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
import org.apache.calcite.sql.type.SqlOperandCountRanges;
import org.apache.calcite.sql.type.SqlOperandTypeChecker;
+import org.apache.calcite.sql.type.SqlTypeTransforms;
import org.apache.calcite.sql.validate.SqlValidator;
/**
@@ -37,7 +38,8 @@ public class SqlJsonDepthFunction extends SqlFunction {
public SqlJsonDepthFunction() {
super("JSON_DEPTH",
SqlKind.OTHER_FUNCTION,
- ReturnTypes.INTEGER_NULLABLE,
+ ReturnTypes.cascade(ReturnTypes.INTEGER,
+ SqlTypeTransforms.FORCE_NULLABLE),
null,
OperandTypes.ANY,
SqlFunctionCategory.SYSTEM);
diff --git
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonKeysFunction.java
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonKeysFunction.java
index 8cd6444..17c4507 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonKeysFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonKeysFunction.java
@@ -16,17 +16,12 @@
*/
package org.apache.calcite.sql.fun;
-import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlFunction;
import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlKind;
-import org.apache.calcite.sql.SqlLiteral;
-import org.apache.calcite.sql.SqlNode;
-import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
-import org.apache.calcite.sql.type.SqlOperandTypeChecker;
-import org.apache.calcite.sql.validate.SqlValidator;
+import org.apache.calcite.sql.type.SqlTypeFamily;
/**
* The <code>JSON_KEYS</code> function.
@@ -36,19 +31,10 @@ public class SqlJsonKeysFunction extends SqlFunction {
super("JSON_KEYS", SqlKind.OTHER_FUNCTION,
ReturnTypes.VARCHAR_2000,
null,
- OperandTypes.ANY,
+ OperandTypes.or(OperandTypes.ANY,
+ OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.CHARACTER)),
SqlFunctionCategory.SYSTEM);
}
-
- @Override protected void checkOperandCount(SqlValidator validator,
- SqlOperandTypeChecker argType, SqlCall call) {
- assert call.operandCount() == 1;
- }
-
- @Override public SqlCall createCall(SqlLiteral functionQualifier,
- SqlParserPos pos, SqlNode... operands)
{
- return super.createCall(functionQualifier, pos, operands);
- }
}
// End SqlJsonKeysFunction.java
diff --git
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonLengthFunction.java
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonLengthFunction.java
index 4b9308a..ee911d1 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonLengthFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonLengthFunction.java
@@ -16,16 +16,12 @@
*/
package org.apache.calcite.sql.fun;
-import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlFunction;
import org.apache.calcite.sql.SqlFunctionCategory;
import org.apache.calcite.sql.SqlKind;
-import org.apache.calcite.sql.SqlLiteral;
-import org.apache.calcite.sql.SqlNode;
-import org.apache.calcite.sql.SqlWriter;
-import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
+import org.apache.calcite.sql.type.SqlTypeFamily;
import org.apache.calcite.sql.type.SqlTypeTransforms;
/**
@@ -37,21 +33,10 @@ public class SqlJsonLengthFunction extends SqlFunction {
ReturnTypes.cascade(ReturnTypes.INTEGER,
SqlTypeTransforms.FORCE_NULLABLE),
null,
- OperandTypes.ANY,
+ OperandTypes.or(OperandTypes.ANY,
+ OperandTypes.family(SqlTypeFamily.ANY, SqlTypeFamily.CHARACTER)),
SqlFunctionCategory.SYSTEM);
}
-
- @Override public void unparse(SqlWriter writer, SqlCall call, int leftPrec,
- int rightPrec) {
- final SqlWriter.Frame frame = writer.startFunCall(getName());
- call.operand(0).unparse(writer, 0, 0);
- writer.endFunCall(frame);
- }
-
- @Override public SqlCall createCall(SqlLiteral functionQualifier,
- SqlParserPos pos, SqlNode... operands) {
- return super.createCall(functionQualifier, pos, operands);
- }
}
// End SqlJsonLengthFunction.java
diff --git
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonObjectAggAggFunction.java
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonObjectAggAggFunction.java
index 3955e0d..d1e8d63 100644
---
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonObjectAggAggFunction.java
+++
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonObjectAggAggFunction.java
@@ -17,6 +17,7 @@
package org.apache.calcite.sql.fun;
import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.sql.SqlAggFunction;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlFunctionCategory;
@@ -27,6 +28,7 @@ import org.apache.calcite.sql.SqlWriter;
import org.apache.calcite.sql.type.OperandTypes;
import org.apache.calcite.sql.type.ReturnTypes;
import org.apache.calcite.sql.type.SqlTypeFamily;
+import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.validate.SqlValidator;
import org.apache.calcite.sql.validate.SqlValidatorImpl;
import org.apache.calcite.sql.validate.SqlValidatorScope;
@@ -43,8 +45,14 @@ public class SqlJsonObjectAggAggFunction extends
SqlAggFunction {
/** Creates a SqlJsonObjectAggAggFunction. */
public SqlJsonObjectAggAggFunction(SqlKind kind,
SqlJsonConstructorNullClause nullClause) {
- super(kind + "_" + nullClause.name(), null, kind,
ReturnTypes.VARCHAR_2000, null,
- OperandTypes.family(SqlTypeFamily.CHARACTER, SqlTypeFamily.ANY),
+ super(kind + "_" + nullClause.name(), null, kind, ReturnTypes.VARCHAR_2000,
+ (callBinding, returnType, operandTypes) -> {
+ RelDataTypeFactory typeFactory = callBinding.getTypeFactory();
+ operandTypes[0] = typeFactory.createSqlType(SqlTypeName.VARCHAR);
+ operandTypes[1] = typeFactory.createTypeWithNullability(
+ typeFactory.createSqlType(SqlTypeName.ANY), true);
+ }, OperandTypes.family(SqlTypeFamily.CHARACTER,
+ SqlTypeFamily.ANY),
SqlFunctionCategory.SYSTEM, false, false, Optionality.FORBIDDEN);
this.nullClause = Objects.requireNonNull(nullClause);
}
diff --git
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonObjectFunction.java
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonObjectFunction.java
index 0240532..f74d577 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonObjectFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonObjectFunction.java
@@ -17,6 +17,7 @@
package org.apache.calcite.sql.fun;
import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlCallBinding;
import org.apache.calcite.sql.SqlFunction;
@@ -31,6 +32,7 @@ import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.type.ReturnTypes;
import org.apache.calcite.sql.type.SqlOperandCountRanges;
import org.apache.calcite.sql.type.SqlOperandTypeChecker;
+import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.type.SqlTypeUtil;
import org.apache.calcite.sql.validate.SqlValidator;
@@ -43,8 +45,18 @@ import static org.apache.calcite.util.Static.RESOURCE;
*/
public class SqlJsonObjectFunction extends SqlFunction {
public SqlJsonObjectFunction() {
- super("JSON_OBJECT", SqlKind.OTHER_FUNCTION, ReturnTypes.VARCHAR_2000,
null,
- null, SqlFunctionCategory.SYSTEM);
+ super("JSON_OBJECT", SqlKind.OTHER_FUNCTION, ReturnTypes.VARCHAR_2000,
+ (callBinding, returnType, operandTypes) -> {
+ RelDataTypeFactory typeFactory = callBinding.getTypeFactory();
+ for (int i = 0; i < operandTypes.length; i++) {
+ if (i % 2 == 0) {
+ operandTypes[i] = typeFactory.createSqlType(SqlTypeName.VARCHAR);
+ continue;
+ }
+ operandTypes[i] = typeFactory.createTypeWithNullability(
+ typeFactory.createSqlType(SqlTypeName.ANY), true);
+ }
+ }, null, SqlFunctionCategory.SYSTEM);
}
@Override public SqlOperandCountRange getOperandCountRange() {
diff --git
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonValueExpressionOperator.java
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonValueExpressionOperator.java
index fbad8e1..6ea1f8f 100644
---
a/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonValueExpressionOperator.java
+++
b/core/src/main/java/org/apache/calcite/sql/fun/SqlJsonValueExpressionOperator.java
@@ -16,46 +16,23 @@
*/
package org.apache.calcite.sql.fun;
-import org.apache.calcite.rel.type.RelDataTypeFactory;
-import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlKind;
-import org.apache.calcite.sql.SqlSpecialOperator;
-import org.apache.calcite.sql.SqlWriter;
+import org.apache.calcite.sql.SqlPostfixOperator;
import org.apache.calcite.sql.type.OperandTypes;
+import org.apache.calcite.sql.type.ReturnTypes;
import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.sql.type.SqlTypeTransforms;
/**
* The JSON value expression operator that indicates that the value expression
* should be parsed as JSON.
*/
-public class SqlJsonValueExpressionOperator extends SqlSpecialOperator {
- private final boolean structured;
+public class SqlJsonValueExpressionOperator extends SqlPostfixOperator {
- public SqlJsonValueExpressionOperator(String name, boolean structured) {
- super(name, SqlKind.JSON_VALUE_EXPRESSION, 100, true,
- opBinding -> {
- final RelDataTypeFactory typeFactory = opBinding.getTypeFactory();
- return typeFactory.createTypeWithNullability(
- typeFactory.createSqlType(SqlTypeName.ANY), true);
- },
- (callBinding, returnType, operandTypes) -> {
- if (callBinding.isOperandNull(0, false)) {
- final RelDataTypeFactory typeFactory =
- callBinding.getTypeFactory();
- operandTypes[0] = typeFactory.createTypeWithNullability(
- typeFactory.createSqlType(SqlTypeName.ANY), true);
- }
- },
- structured ? OperandTypes.ANY : OperandTypes.STRING);
- this.structured = structured;
- }
-
- @Override public void unparse(SqlWriter writer, SqlCall call, int leftPrec,
- int rightPrec) {
- call.operand(0).unparse(writer, 0, 0);
- if (!structured) {
- writer.keyword("FORMAT JSON");
- }
+ public SqlJsonValueExpressionOperator() {
+ super("FORMAT JSON", SqlKind.JSON_VALUE_EXPRESSION, 28,
+ ReturnTypes.cascade(ReturnTypes.explicit(SqlTypeName.ANY),
+ SqlTypeTransforms.TO_NULLABLE), null, OperandTypes.CHARACTER);
}
}
diff --git
a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
index 8654f7d..81f08c8 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
@@ -807,6 +807,9 @@ public class SqlStdOperatorTable extends
ReflectiveSqlOperatorTable {
null,
OperandTypes.CHARACTER);
+ public static final SqlPostfixOperator JSON_VALUE_EXPRESSION =
+ new SqlJsonValueExpressionOperator();
+
//-------------------------------------------------------------
// PREFIX OPERATORS
@@ -1291,18 +1294,8 @@ public class SqlStdOperatorTable extends
ReflectiveSqlOperatorTable {
public static final SqlThrowOperator THROW = new SqlThrowOperator();
- public static final SqlJsonValueExpressionOperator JSON_VALUE_EXPRESSION =
- new SqlJsonValueExpressionOperator("JSON_VALUE_EXPRESSION", false);
-
- public static final SqlJsonValueExpressionOperator
JSON_STRUCTURED_VALUE_EXPRESSION =
- new SqlJsonValueExpressionOperator("JSON_STRUCTURED_VALUE_EXPRESSION",
- true);
-
public static final SqlJsonApiCommonSyntaxOperator JSON_API_COMMON_SYNTAX =
- new SqlJsonApiCommonSyntaxOperator("JSON_API_COMMON_SYNTAX", true);
-
- public static final SqlJsonApiCommonSyntaxOperator
JSON_API_COMMON_SYNTAX_WITHOUT_PATH =
- new
SqlJsonApiCommonSyntaxOperator("JSON_API_COMMON_SYNTAX_WITHOUT_PATH", false);
+ new SqlJsonApiCommonSyntaxOperator("JSON_API_COMMON_SYNTAX");
public static final SqlFunction JSON_EXISTS = new SqlJsonExistsFunction();
diff --git a/core/src/main/java/org/apache/calcite/sql/type/InferTypes.java
b/core/src/main/java/org/apache/calcite/sql/type/InferTypes.java
index 10dd95c..9e78a08 100644
--- a/core/src/main/java/org/apache/calcite/sql/type/InferTypes.java
+++ b/core/src/main/java/org/apache/calcite/sql/type/InferTypes.java
@@ -104,6 +104,20 @@ public abstract class InferTypes {
}
};
+ /**
+ * Operand type-inference strategy where an unknown operand type is assumed
+ * to be nullable ANY.
+ */
+ public static final SqlOperandTypeInference ANY_NULLABLE =
+ (callBinding, returnType, operandTypes) -> {
+ RelDataTypeFactory typeFactory = callBinding.getTypeFactory();
+ for (int i = 0; i < operandTypes.length; ++i) {
+ operandTypes[i] =
+ typeFactory.createTypeWithNullability(
+ typeFactory.createSqlType(SqlTypeName.ANY), true);
+ }
+ };
+
/** Returns an {@link SqlOperandTypeInference} that returns a given list of
* types. */
public static SqlOperandTypeInference explicit(List<RelDataType> types) {
diff --git
a/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
b/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
index 6f783f9..aa0db73 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/StandardConvertletTable.java
@@ -45,6 +45,7 @@ import org.apache.calcite.sql.SqlNode;
import org.apache.calcite.sql.SqlNodeList;
import org.apache.calcite.sql.SqlNumericLiteral;
import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.SqlPostfixOperator;
import org.apache.calcite.sql.SqlUtil;
import org.apache.calcite.sql.fun.OracleSqlOperatorTable;
import org.apache.calcite.sql.fun.SqlArrayValueConstructor;
@@ -77,9 +78,12 @@ import com.google.common.collect.Lists;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import static org.apache.calcite.util.Static.RESOURCE;
+
/**
* Standard implementation of {@link SqlRexConvertletTable}.
*/
@@ -116,8 +120,8 @@ public class StandardConvertletTable extends
ReflectiveConvertletTable {
registerOp(SqlStdOperatorTable.MINUS,
(cx, call) -> {
final RexCall e =
- (RexCall) StandardConvertletTable.this.convertCall(cx, call,
- call.getOperator());
+ (RexCall) StandardConvertletTable.this.convertCall(cx,
call.getOperator(),
+ call.getOperandList());
switch (e.getOperands().get(0).getType().getSqlTypeName()) {
case DATE:
case TIME:
@@ -209,6 +213,19 @@ public class StandardConvertletTable extends
ReflectiveConvertletTable {
call.operand(0),
SqlLiteral.createExactNumeric("0.5", SqlParserPos.ZERO))));
+ registerOp(SqlStdOperatorTable.JSON_API_COMMON_SYNTAX,
+ new JsonOperatorValueExprConvertlet(0));
+ registerOp(SqlStdOperatorTable.JSON_PRETTY,
+ new JsonOperatorValueExprConvertlet(0));
+ registerOp(SqlStdOperatorTable.JSON_TYPE,
+ new JsonOperatorValueExprConvertlet(0));
+ registerOp(SqlStdOperatorTable.JSON_DEPTH,
+ new JsonOperatorValueExprConvertlet(0));
+ registerOp(SqlStdOperatorTable.JSON_LENGTH,
+ new JsonOperatorValueExprConvertlet(0));
+ registerOp(SqlStdOperatorTable.JSON_KEYS,
+ new JsonOperatorValueExprConvertlet(0));
+
// Convert json_value('{"foo":"bar"}', 'lax $.foo', returning
varchar(2000))
// to cast(json_value('{"foo":"bar"}', 'lax $.foo') as varchar(2000))
registerOp(
@@ -747,16 +764,13 @@ public class StandardConvertletTable extends
ReflectiveConvertletTable {
public RexNode convertCall(
SqlRexContext cx,
SqlCall call) {
- return convertCall(cx, call, call.getOperator());
+ return convertCall(cx, call.getOperator(), call.getOperandList());
}
/** Converts a {@link SqlCall} to a {@link RexCall} with a perhaps different
* operator. */
private RexNode convertCall(
- SqlRexContext cx,
- SqlCall call,
- SqlOperator op) {
- final List<SqlNode> operands = call.getOperandList();
+ SqlRexContext cx, SqlOperator op, List<SqlNode> operands) {
final RexBuilder rexBuilder = cx.getRexBuilder();
final SqlOperandTypeChecker.Consistency consistency =
op.getOperandTypeChecker() == null
@@ -1432,6 +1446,58 @@ public class StandardConvertletTable extends
ReflectiveConvertletTable {
}
}
+ /** Convertlet that adds implicit calls to
+ * {@link org.apache.calcite.sql.fun.SqlJsonValueExpressionOperator}
+ * for a series fo JSON operators.
+ *
+ * <p> A call to JSON operator sometimes has a specific expression
+ * argument (it's always the first argument of the call), which implies
+ * a "FORMAT JSON" postfix when the argument is not a explicit call to the
+ * {@link org.apache.calcite.sql.fun.SqlJsonValueExpressionOperator}. */
+ private class JsonOperatorValueExprConvertlet implements SqlRexConvertlet {
+ private final int[] implicitJsonValueExprOrdinals;
+
+ private JsonOperatorValueExprConvertlet(int[]
implicitJsonValueExprOrdinals) {
+ this.implicitJsonValueExprOrdinals = implicitJsonValueExprOrdinals;
+ }
+
+ private JsonOperatorValueExprConvertlet(int implicitJsonValueExprOrdinal) {
+ this(new int[]{implicitJsonValueExprOrdinal});
+ }
+
+ public RexNode convertCall(SqlRexContext cx, SqlCall call) {
+ List<SqlNode> newOperands = new ArrayList<>(call.getOperandList());
+ for (int ordinal : implicitJsonValueExprOrdinals) {
+ assert ordinal < newOperands.size();
+ final SqlNode operand = call.operand(ordinal);
+ if (operand.getKind() != SqlKind.JSON_VALUE_EXPRESSION) {
+ final SqlPostfixOperator newOp =
SqlStdOperatorTable.JSON_VALUE_EXPRESSION;
+
+ // Validate the original operand using the new operator. JSON value
expression
+ // accepts only a character input.
+ //
+ // Once CALCITE-2869 is fixed, we can validate the original operand
during SQL
+ // validation using SqlTypeName.JSON.
+ RelDataType validateOperandType =
cx.getValidator().getValidatedNodeType(operand);
+ assert validateOperandType != null;
+ if (!SqlTypeUtil.inCharFamily(validateOperandType)) {
+ // must be character type
+ throw cx.getValidator().newValidationError(call,
+ RESOURCE.canNotApplyOp2Type(newOp.getName(),
+ SqlUtil.getOperatorSignature(newOp,
+ Collections.singletonList(validateOperandType)),
+ newOp.getAllowedSignatures()));
+ }
+
+ final SqlNode replacement = newOp
+ .createCall(SqlParserPos.ZERO, operand);
+ newOperands.set(ordinal, replacement);
+ }
+ }
+ return StandardConvertletTable.this.convertCall(cx, call.getOperator(),
newOperands);
+ }
+ }
+
/** Convertlet that handles the {@code TIMESTAMPADD} function. */
private static class TimestampAddConvertlet implements SqlRexConvertlet {
public RexNode convertCall(SqlRexContext cx, SqlCall call) {
diff --git a/core/src/main/java/org/apache/calcite/util/Bug.java
b/core/src/main/java/org/apache/calcite/util/Bug.java
index 2c16bd7..6e167a0 100644
--- a/core/src/main/java/org/apache/calcite/util/Bug.java
+++ b/core/src/main/java/org/apache/calcite/util/Bug.java
@@ -175,6 +175,11 @@ public abstract class Bug {
public static final boolean CALCITE_2776_FIXED = false;
/** Whether
+ * <a
href="https://issues.apache.org/jira/browse/CALCITE-2869">[CALCITE-2869]
+ * JSON data type support</a> is fixed. */
+ public static final boolean CALCITE_2869_FIXED = false;
+
+ /** Whether
* <a
href="https://issues.apache.org/jira/browse/CALCITE-2933">[CALCITE-2933]
* In Druid adapter, expression like "cast(cast(\"timestamp\" as timestamp)
as varchar)"
* returns as epoch millisecond</a> is fixed. */
diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
index 9d2de30..f06e2f5 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -271,12 +271,8 @@ public enum BuiltInMethod {
DEJSONIZE(SqlFunctions.class, "dejsonize", String.class),
JSON_VALUE_EXPRESSION(SqlFunctions.class, "jsonValueExpression",
String.class),
- JSON_STRUCTURED_VALUE_EXPRESSION(SqlFunctions.class,
- "jsonStructuredValueExpression", Object.class),
JSON_API_COMMON_SYNTAX(SqlFunctions.class, "jsonApiCommonSyntax",
Object.class, String.class),
- JSON_API_COMMON_SYNTAX_WITHOUT_PATH(SqlFunctions.class,
"jsonApiCommonSyntax",
- Object.class),
JSON_EXISTS(SqlFunctions.class, "jsonExists", Object.class),
JSON_VALUE_ANY(SqlFunctions.class, "jsonValueAny", Object.class,
SqlJsonValueEmptyOrErrorBehavior.class, Object.class,
diff --git
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
index b82b52c..5ae565e 100644
---
a/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
+++
b/core/src/test/java/org/apache/calcite/rel/rel2sql/RelToSqlConverterTest.java
@@ -3147,6 +3147,19 @@ public class RelToSqlConverterTest {
sql(query).ok(expected);
}
+ @Test public void testJsonValueExpressionOperator() {
+ String query = "select \"product_name\" format json, "
+ + "\"product_name\" format json encoding utf8, "
+ + "\"product_name\" format json encoding utf16, "
+ + "\"product_name\" format json encoding utf32 from \"product\"";
+ final String expected = "SELECT \"product_name\" FORMAT JSON, "
+ + "\"product_name\" FORMAT JSON, "
+ + "\"product_name\" FORMAT JSON, "
+ + "\"product_name\" FORMAT JSON\n"
+ + "FROM \"foodmart\".\"product\"";
+ sql(query).ok(expected);
+ }
+
@Test public void testJsonExists() {
String query = "select json_exists(\"product_name\", 'lax $') from
\"product\"";
final String expected = "SELECT JSON_EXISTS(\"product_name\" FORMAT JSON,
'lax $')\n"
diff --git
a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
index bfb0291..bee50f9 100644
--- a/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/parser/SqlParserTest.java
@@ -721,8 +721,8 @@ public class SqlParserTest {
"(?s).*Encountered \".1\" at line 1, column 13.\n"
+ "Was expecting one of:\n"
+ " <EOF> \n"
- + " \"AND\" \\.\\.\\.\n"
+ " \"AS\" \\.\\.\\.\n"
+ + " \"EXCEPT\" \\.\\.\\.\n"
+ ".*");
}
@@ -6691,8 +6691,8 @@ public class SqlParserTest {
"(?s)Encountered \"to year\" at line 1, column 19.\n"
+ "Was expecting one of:\n"
+ " <EOF> \n"
- + " \"AND\" \\.\\.\\.\n"
- + " \"BETWEEN\" \\.\\.\\..*");
+ + " \"\\(\" \\.\\.\\.\n"
+ + " \"\\.\" \\.\\.\\..*");
checkExpFails("interval '1-2' year ^to^ day", ANY);
checkExpFails("interval '1-2' year ^to^ hour", ANY);
checkExpFails("interval '1-2' year ^to^ minute", ANY);
@@ -8357,64 +8357,83 @@ public class SqlParserTest {
sql(sql).ok(expected);
}
+ @Test public void testJsonValueExpressionOperator() {
+ checkExp("foo format json",
+ "`FOO` FORMAT JSON");
+ // Currently, encoding js not valid
+ checkExp("foo format json encoding utf8",
+ "`FOO` FORMAT JSON");
+ checkExp("foo format json encoding utf16",
+ "`FOO` FORMAT JSON");
+ checkExp("foo format json encoding utf32",
+ "`FOO` FORMAT JSON");
+ checkExp("null format json", "NULL FORMAT JSON");
+ // Test case to eliminate choice conflict on token <FORMAT>
+ check("select foo format from tab", "SELECT `FOO` AS `FORMAT`\n"
+ + "FROM `TAB`");
+ // Test case to eliminate choice conflict on token <ENCODING>
+ check("select foo format json encoding from tab", "SELECT `FOO` FORMAT
JSON AS `ENCODING`\n"
+ + "FROM `TAB`");
+ }
+
@Test public void testJsonExists() {
checkExp("json_exists('{\"foo\": \"bar\"}', 'lax $.foo')",
- "JSON_EXISTS('{\"foo\": \"bar\"}' FORMAT JSON, 'lax $.foo')");
+ "JSON_EXISTS('{\"foo\": \"bar\"}', 'lax $.foo')");
checkExp("json_exists('{\"foo\": \"bar\"}', 'lax $.foo' error on error)",
- "JSON_EXISTS('{\"foo\": \"bar\"}' FORMAT JSON, 'lax $.foo' ERROR ON
ERROR)");
+ "JSON_EXISTS('{\"foo\": \"bar\"}', 'lax $.foo' ERROR ON ERROR)");
}
@Test public void testJsonValue() {
checkExp("json_value('{\"foo\": \"100\"}', 'lax $.foo' "
+ "returning integer)",
- "JSON_VALUE('{\"foo\": \"100\"}' FORMAT JSON, 'lax $.foo' "
+ "JSON_VALUE('{\"foo\": \"100\"}', 'lax $.foo' "
+ "RETURNING INTEGER NULL ON EMPTY NULL ON ERROR)");
checkExp("json_value('{\"foo\": \"100\"}', 'lax $.foo' "
+ "returning integer default 10 on empty error on error)",
- "JSON_VALUE('{\"foo\": \"100\"}' FORMAT JSON, 'lax $.foo' "
+ "JSON_VALUE('{\"foo\": \"100\"}', 'lax $.foo' "
+ "RETURNING INTEGER DEFAULT 10 ON EMPTY ERROR ON ERROR)");
}
@Test public void testJsonQuery() {
checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' WITHOUT ARRAY WRAPPER)",
- "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+ "JSON_QUERY('{\"foo\": \"bar\"}', "
+ "'lax $' WITHOUT ARRAY WRAPPER NULL ON EMPTY NULL ON ERROR)");
checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' WITH WRAPPER)",
- "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+ "JSON_QUERY('{\"foo\": \"bar\"}', "
+ "'lax $' WITH UNCONDITIONAL ARRAY WRAPPER NULL ON EMPTY NULL ON
ERROR)");
checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' WITH UNCONDITIONAL
WRAPPER)",
- "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+ "JSON_QUERY('{\"foo\": \"bar\"}', "
+ "'lax $' WITH UNCONDITIONAL ARRAY WRAPPER NULL ON EMPTY NULL ON
ERROR)");
checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' WITH CONDITIONAL
WRAPPER)",
- "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+ "JSON_QUERY('{\"foo\": \"bar\"}', "
+ "'lax $' WITH CONDITIONAL ARRAY WRAPPER NULL ON EMPTY NULL ON
ERROR)");
checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' NULL ON EMPTY)",
- "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+ "JSON_QUERY('{\"foo\": \"bar\"}', "
+ "'lax $' WITHOUT ARRAY WRAPPER NULL ON EMPTY NULL ON ERROR)");
checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' ERROR ON EMPTY)",
- "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+ "JSON_QUERY('{\"foo\": \"bar\"}', "
+ "'lax $' WITHOUT ARRAY WRAPPER ERROR ON EMPTY NULL ON ERROR)");
checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' EMPTY ARRAY ON EMPTY)",
- "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+ "JSON_QUERY('{\"foo\": \"bar\"}', "
+ "'lax $' WITHOUT ARRAY WRAPPER EMPTY ARRAY ON EMPTY NULL ON
ERROR)");
checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' EMPTY OBJECT ON EMPTY)",
- "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+ "JSON_QUERY('{\"foo\": \"bar\"}', "
+ "'lax $' WITHOUT ARRAY WRAPPER EMPTY OBJECT ON EMPTY NULL ON
ERROR)");
checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' NULL ON ERROR)",
- "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+ "JSON_QUERY('{\"foo\": \"bar\"}', "
+ "'lax $' WITHOUT ARRAY WRAPPER NULL ON EMPTY NULL ON ERROR)");
checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' ERROR ON ERROR)",
- "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+ "JSON_QUERY('{\"foo\": \"bar\"}', "
+ "'lax $' WITHOUT ARRAY WRAPPER NULL ON EMPTY ERROR ON ERROR)");
checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' EMPTY ARRAY ON ERROR)",
- "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+ "JSON_QUERY('{\"foo\": \"bar\"}', "
+ "'lax $' WITHOUT ARRAY WRAPPER NULL ON EMPTY EMPTY ARRAY ON
ERROR)");
checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' EMPTY OBJECT ON ERROR)",
- "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+ "JSON_QUERY('{\"foo\": \"bar\"}', "
+ "'lax $' WITHOUT ARRAY WRAPPER NULL ON EMPTY EMPTY OBJECT ON
ERROR)");
checkExp("json_query('{\"foo\": \"bar\"}', 'lax $' EMPTY ARRAY ON EMPTY "
+ "EMPTY OBJECT ON ERROR)",
- "JSON_QUERY('{\"foo\": \"bar\"}' FORMAT JSON, "
+ "JSON_QUERY('{\"foo\": \"bar\"}', "
+ "'lax $' WITHOUT ARRAY WRAPPER EMPTY ARRAY ON EMPTY EMPTY OBJECT
ON ERROR)");
}
@@ -8438,43 +8457,43 @@ public class SqlParserTest {
}
@Test public void testJsonType() {
- checkExp("json_type('11.56')", "JSON_TYPE('11.56' FORMAT JSON)");
- checkExp("json_type('{}')", "JSON_TYPE('{}' FORMAT JSON)");
- checkExp("json_type(null)", "JSON_TYPE(NULL FORMAT JSON)");
+ checkExp("json_type('11.56')", "JSON_TYPE('11.56')");
+ checkExp("json_type('{}')", "JSON_TYPE('{}')");
+ checkExp("json_type(null)", "JSON_TYPE(NULL)");
checkExp("json_type('[\"foo\",null]')",
- "JSON_TYPE('[\"foo\",null]' FORMAT JSON)");
+ "JSON_TYPE('[\"foo\",null]')");
checkExp("json_type('{\"foo\": \"100\"}')",
- "JSON_TYPE('{\"foo\": \"100\"}' FORMAT JSON)");
+ "JSON_TYPE('{\"foo\": \"100\"}')");
}
@Test public void testJsonDepth() {
- checkExp("json_depth('11.56')", "JSON_DEPTH('11.56' FORMAT JSON)");
- checkExp("json_depth('{}')", "JSON_DEPTH('{}' FORMAT JSON)");
- checkExp("json_depth(null)", "JSON_DEPTH(NULL FORMAT JSON)");
+ checkExp("json_depth('11.56')", "JSON_DEPTH('11.56')");
+ checkExp("json_depth('{}')", "JSON_DEPTH('{}')");
+ checkExp("json_depth(null)", "JSON_DEPTH(NULL)");
checkExp("json_depth('[\"foo\",null]')",
- "JSON_DEPTH('[\"foo\",null]' FORMAT JSON)");
+ "JSON_DEPTH('[\"foo\",null]')");
checkExp("json_depth('{\"foo\": \"100\"}')",
- "JSON_DEPTH('{\"foo\": \"100\"}' FORMAT JSON)");
+ "JSON_DEPTH('{\"foo\": \"100\"}')");
}
@Test public void testJsonLength() {
checkExp("json_length('{\"foo\": \"bar\"}')",
- "JSON_LENGTH('{\"foo\": \"bar\"}' FORMAT JSON)");
+ "JSON_LENGTH('{\"foo\": \"bar\"}')");
checkExp("json_length('{\"foo\": \"bar\"}', 'lax $')",
- "JSON_LENGTH('{\"foo\": \"bar\"}' FORMAT JSON, 'lax $')");
+ "JSON_LENGTH('{\"foo\": \"bar\"}', 'lax $')");
checkExp("json_length('{\"foo\": \"bar\"}', 'strict $')",
- "JSON_LENGTH('{\"foo\": \"bar\"}' FORMAT JSON, 'strict $')");
+ "JSON_LENGTH('{\"foo\": \"bar\"}', 'strict $')");
checkExp("json_length('{\"foo\": \"bar\"}', 'invalid $')",
- "JSON_LENGTH('{\"foo\": \"bar\"}' FORMAT JSON, 'invalid $')");
+ "JSON_LENGTH('{\"foo\": \"bar\"}', 'invalid $')");
}
@Test public void testJsonKeys() {
checkExp("json_keys('{\"foo\": \"bar\"}', 'lax $')",
- "JSON_KEYS('{\"foo\": \"bar\"}' FORMAT JSON, 'lax $')");
+ "JSON_KEYS('{\"foo\": \"bar\"}', 'lax $')");
checkExp("json_keys('{\"foo\": \"bar\"}', 'strict $')",
- "JSON_KEYS('{\"foo\": \"bar\"}' FORMAT JSON, 'strict $')");
+ "JSON_KEYS('{\"foo\": \"bar\"}', 'strict $')");
checkExp("json_keys('{\"foo\": \"bar\"}', 'invalid $')",
- "JSON_KEYS('{\"foo\": \"bar\"}' FORMAT JSON, 'invalid $')");
+ "JSON_KEYS('{\"foo\": \"bar\"}', 'invalid $')");
}
@Test public void testJsonObjectAgg() {
@@ -8507,9 +8526,9 @@ public class SqlParserTest {
@Test public void testJsonPretty() {
checkExp("json_pretty('foo')",
- "JSON_PRETTY('foo' FORMAT JSON)");
+ "JSON_PRETTY('foo')");
checkExp("json_pretty(null)",
- "JSON_PRETTY(NULL FORMAT JSON)");
+ "JSON_PRETTY(NULL)");
}
@Test public void testJsonArrayAgg1() {
diff --git a/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
b/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
index aa3d706..a31e5b6 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlAdvisorTest.java
@@ -292,6 +292,7 @@ public class SqlAdvisorTest extends SqlValidatorTestCase {
"KEYWORD(BETWEEN)",
"KEYWORD(CONTAINS)",
"KEYWORD(EQUALS)",
+ "KEYWORD(FORMAT)",
"KEYWORD(IMMEDIATELY)",
"KEYWORD(IN)",
"KEYWORD(IS)",
diff --git
a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
index 4c77dde..c7847f6 100644
--- a/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
+++ b/core/src/test/java/org/apache/calcite/sql/test/SqlOperatorBaseTest.java
@@ -4462,26 +4462,6 @@ public abstract class SqlOperatorBaseTest {
}
- @Test public void testJsonObject() {
- tester.checkString("json_object()", "{}", "VARCHAR(2000) NOT NULL");
- tester.checkString("json_object('foo': 'bar')",
- "{\"foo\":\"bar\"}", "VARCHAR(2000) NOT NULL");
- tester.checkString("json_object('foo': 'bar', 'foo2': 'bar2')",
- "{\"foo\":\"bar\",\"foo2\":\"bar2\"}", "VARCHAR(2000) NOT NULL");
- tester.checkString("json_object('foo': null)",
- "{\"foo\":null}", "VARCHAR(2000) NOT NULL");
- tester.checkString("json_object('foo': null null on null)",
- "{\"foo\":null}", "VARCHAR(2000) NOT NULL");
- tester.checkString("json_object('foo': null absent on null)",
- "{}", "VARCHAR(2000) NOT NULL");
- tester.checkString("json_object('foo': 100)",
- "{\"foo\":100}", "VARCHAR(2000) NOT NULL");
- tester.checkString("json_object('foo': json_object('foo': 'bar'))",
- "{\"foo\":\"{\\\"foo\\\":\\\"bar\\\"}\"}", "VARCHAR(2000) NOT NULL");
- tester.checkString("json_object('foo': json_object('foo': 'bar') format
json)",
- "{\"foo\":{\"foo\":\"bar\"}}", "VARCHAR(2000) NOT NULL");
- }
-
@Test public void testJsonPretty() {
tester.checkString("json_pretty('{\"foo\":100}')",
"{\n \"foo\" : 100\n}", "VARCHAR(2000) NOT NULL");
@@ -4643,8 +4623,29 @@ public abstract class SqlOperatorBaseTest {
"(?s).*No results for path.*", true);
}
+ @Test public void testJsonObject() {
+ tester.checkString("json_object()", "{}", "VARCHAR(2000) NOT NULL");
+ tester.checkString("json_object('foo': 'bar')",
+ "{\"foo\":\"bar\"}", "VARCHAR(2000) NOT NULL");
+ tester.checkString("json_object('foo': 'bar', 'foo2': 'bar2')",
+ "{\"foo\":\"bar\",\"foo2\":\"bar2\"}", "VARCHAR(2000) NOT NULL");
+ tester.checkString("json_object('foo': null)",
+ "{\"foo\":null}", "VARCHAR(2000) NOT NULL");
+ tester.checkString("json_object('foo': null null on null)",
+ "{\"foo\":null}", "VARCHAR(2000) NOT NULL");
+ tester.checkString("json_object('foo': null absent on null)",
+ "{}", "VARCHAR(2000) NOT NULL");
+ tester.checkString("json_object('foo': 100)",
+ "{\"foo\":100}", "VARCHAR(2000) NOT NULL");
+ tester.checkString("json_object('foo': json_object('foo': 'bar'))",
+ "{\"foo\":\"{\\\"foo\\\":\\\"bar\\\"}\"}", "VARCHAR(2000) NOT NULL");
+ tester.checkString("json_object('foo': json_object('foo': 'bar') format
json)",
+ "{\"foo\":{\"foo\":\"bar\"}}", "VARCHAR(2000) NOT NULL");
+ }
+
@Test public void testJsonObjectAgg() {
checkAggType(tester, "json_objectagg('foo': 'bar')", "VARCHAR(2000) NOT
NULL");
+ checkAggType(tester, "json_objectagg('foo': null)", "VARCHAR(2000) NOT
NULL");
tester.checkFails("^json_objectagg(100: 'bar')^",
"(?s).*Cannot apply.*", false);
final String[][] values = {
@@ -4688,6 +4689,7 @@ public abstract class SqlOperatorBaseTest {
@Test public void testJsonArrayAgg() {
checkAggType(tester, "json_arrayagg('foo')", "VARCHAR(2000) NOT NULL");
+ checkAggType(tester, "json_arrayagg(null)", "VARCHAR(2000) NOT NULL");
final String[] values = {
"'foo'",
"cast(null as varchar(2000))",
diff --git
a/core/src/test/java/org/apache/calcite/test/SqlJsonFunctionsTest.java
b/core/src/test/java/org/apache/calcite/test/SqlJsonFunctionsTest.java
index 5d9ca43..bc4fbd0 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlJsonFunctionsTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlJsonFunctionsTest.java
@@ -62,12 +62,6 @@ public class SqlJsonFunctionsTest {
}
@Test
- public void testJsonStructuredValueExpression() {
- assertJsonStructuredValueExpression("bar", is("bar"));
- assertJsonStructuredValueExpression(100, is(100));
- }
-
- @Test
public void testJsonApiCommonSyntax() {
assertJsonApiCommonSyntax(ImmutableMap.of("foo", "bar"), "lax $.foo",
contextMatches(
@@ -604,13 +598,6 @@ public class SqlJsonFunctionsTest {
SqlFunctions.jsonValueExpression(input), matcher);
}
- private void assertJsonStructuredValueExpression(Object input,
- Matcher<Object> matcher) {
- assertThat(
-
invocationDesc(BuiltInMethod.JSON_STRUCTURED_VALUE_EXPRESSION.getMethodName(),
input),
- SqlFunctions.jsonStructuredValueExpression(input), matcher);
- }
-
private void assertJsonApiCommonSyntax(Object input, String pathSpec,
Matcher<? super SqlFunctions.PathContext> matcher) {
assertThat(
@@ -706,7 +693,7 @@ public class SqlJsonFunctionsTest {
matcher);
}
- private void assertJsonLength(Object input,
+ private void assertJsonLength(SqlFunctions.PathContext input,
Matcher<? super Integer> matcher) {
assertThat(
invocationDesc(BuiltInMethod.JSON_LENGTH.getMethodName(), input),
diff --git
a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
index e1a5ef1..dbe51d2 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -3037,6 +3037,16 @@ public class SqlToRelConverterTest extends
SqlToRelTestBase {
sql(sql).with(tester).ok();
}
+ @Test public void testJsonValueExpressionOperator() {
+ final String sql = "select ename format json,\n"
+ + "ename format json encoding utf8,\n"
+ + "ename format json encoding utf16,\n"
+ + "ename format json encoding utf32\n"
+ + "from emp";
+ System.out.println(sql);
+ sql(sql).ok();
+ }
+
@Test public void testJsonExists() {
final String sql = "select json_exists(ename, 'lax $')\n"
+ "from emp";
@@ -3055,6 +3065,36 @@ public class SqlToRelConverterTest extends
SqlToRelTestBase {
sql(sql).ok();
}
+ @Test public void testJsonType() {
+ final String sql = "select json_type(ename)\n"
+ + "from emp";
+ sql(sql).ok();
+ }
+
+ @Test public void testJsonPretty() {
+ final String sql = "select json_pretty(ename)\n"
+ + "from emp";
+ sql(sql).ok();
+ }
+
+ @Test public void testJsonDepth() {
+ final String sql = "select json_depth(ename)\n"
+ + "from emp";
+ sql(sql).ok();
+ }
+
+ @Test public void testJsonLength() {
+ final String sql = "select json_length(ename, 'strict $')\n"
+ + "from emp";
+ sql(sql).ok();
+ }
+
+ @Test public void testJsonKeys() {
+ final String sql = "select json_keys(ename, 'strict $')\n"
+ + "from emp";
+ sql(sql).ok();
+ }
+
@Test public void testJsonArray() {
final String sql = "select json_array(ename, ename)\n"
+ "from emp";
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index b9dad50..a70664c 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -8882,9 +8882,6 @@ public class SqlValidatorTest extends
SqlValidatorTestCase {
+ "DOT -\n"
+ "ITEM -\n"
+ "JSON_API_COMMON_SYNTAX -\n"
- + "JSON_API_COMMON_SYNTAX_WITHOUT_PATH -\n"
- + "JSON_STRUCTURED_VALUE_EXPRESSION -\n"
- + "JSON_VALUE_EXPRESSION -\n"
+ "NEXT_VALUE -\n"
+ "PATTERN_EXCLUDE -\n"
+ "PATTERN_PERMUTE -\n"
@@ -8956,6 +8953,7 @@ public class SqlValidatorTest extends
SqlValidatorTestCase {
+ "SUBMULTISET OF left\n"
+ "SUCCEEDS left\n"
+ "\n"
+ + "FORMAT JSON post\n"
+ "IS A SET post\n"
+ "IS EMPTY post\n"
+ "IS FALSE post\n"
@@ -10886,6 +10884,15 @@ public class SqlValidatorTest extends
SqlValidatorTestCase {
.fails(onError);
}
+ @Test public void testJsonValueExpressionOperator() {
+ checkExp("'{}' format json");
+ checkExp("'{}' format json encoding utf8");
+ checkExp("'{}' format json encoding utf16");
+ checkExp("'{}' format json encoding utf32");
+ checkExpType("'{}' format json", "ANY NOT NULL");
+ checkExpFails("^null^ format json", "(?s).*Illegal use of .NULL.*");
+ }
+
@Test public void testJsonExists() {
checkExp("json_exists('{}', 'lax $')");
checkExpType("json_exists('{}', 'lax $')", "BOOLEAN");
@@ -10952,6 +10959,14 @@ public class SqlValidatorTest extends
SqlValidatorTestCase {
checkExp("json_pretty('{\"foo\":\"bar\"}')");
checkExpType("json_pretty('{\"foo\":\"bar\"}')", "VARCHAR(2000) NOT NULL");
checkFails("select json_pretty(^NULL^) from emp", "(?s).*Illegal use of
.NULL.*");
+
+ if (!Bug.CALCITE_2869_FIXED) {
+ // the case should throw an error but currently validation
+ // is done during sql-to-rel process.
+ //
+ // see StandardConvertletTable.JsonOperatorValueExprConvertlet
+ return;
+ }
checkFails("select json_pretty(^1^) from emp",
"(.*)JSON_VALUE_EXPRESSION(.*)");
}
@@ -10960,6 +10975,10 @@ public class SqlValidatorTest extends
SqlValidatorTestCase {
check("select json_type(ename) from emp");
checkExp("json_type('{\"foo\":\"bar\"}')");
checkExpType("json_type('{\"foo\":\"bar\"}')", "VARCHAR(20) NOT NULL");
+
+ if (!Bug.CALCITE_2869_FIXED) {
+ return;
+ }
checkFails("select json_type(^1^) from emp",
"(.*)JSON_VALUE_EXPRESSION(.*)");
}
@@ -10968,6 +10987,10 @@ public class SqlValidatorTest extends
SqlValidatorTestCase {
check("select json_depth(ename) from emp");
checkExp("json_depth('{\"foo\":\"bar\"}')");
checkExpType("json_depth('{\"foo\":\"bar\"}')", "INTEGER");
+
+ if (!Bug.CALCITE_2869_FIXED) {
+ return;
+ }
checkFails("select json_depth(^1^) from emp",
"(.*)JSON_VALUE_EXPRESSION(.*)");
}
diff --git
a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
index d5c6fcd..a6752d1 100644
--- a/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/SqlToRelConverterTest.xml
@@ -5524,6 +5524,21 @@ LogicalProject(ANYEMPNO=[$1])
]]>
</Resource>
</TestCase>
+ <TestCase name="testJsonValueExpressionOperator">
+ <Resource name="sql">
+ <![CDATA[select ename format json,
+ename format json encoding utf8,
+ename format json encoding utf16,
+ename format json encoding utf32
+from emp]]>
+ </Resource>
+ <Resource name="plan">
+ <![CDATA[
+LogicalProject(EXPR$0=[FORMAT JSON($1)], EXPR$1=[FORMAT JSON($1)],
EXPR$2=[FORMAT JSON($1)], EXPR$3=[FORMAT JSON($1)])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ </TestCase>
<TestCase name="testJsonExists">
<Resource name="sql">
<![CDATA[select json_exists(ename, 'lax $')
@@ -5531,7 +5546,7 @@ from emp]]>
</Resource>
<Resource name="plan">
<![CDATA[
-LogicalProject(EXPR$0=[JSON_EXISTS(JSON_API_COMMON_SYNTAX(JSON_VALUE_EXPRESSION($1),
'lax $'))])
+LogicalProject(EXPR$0=[JSON_EXISTS(JSON_API_COMMON_SYNTAX(FORMAT JSON($1),
'lax $'))])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
@@ -5543,7 +5558,7 @@ from emp]]>
</Resource>
<Resource name="plan">
<![CDATA[
-LogicalProject(EXPR$0=[CAST(JSON_VALUE_ANY(JSON_API_COMMON_SYNTAX(JSON_VALUE_EXPRESSION($1),
'lax $'), FLAG(SqlJsonValueEmptyOrErrorBehavior[NULL]), null:ANY,
FLAG(SqlJsonValueEmptyOrErrorBehavior[NULL]), null:ANY)):VARCHAR(2000)])
+LogicalProject(EXPR$0=[CAST(JSON_VALUE_ANY(JSON_API_COMMON_SYNTAX(FORMAT
JSON($1), 'lax $'), FLAG(SqlJsonValueEmptyOrErrorBehavior[NULL]), null:ANY,
FLAG(SqlJsonValueEmptyOrErrorBehavior[NULL]), null:ANY)):VARCHAR(2000)])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
@@ -5555,7 +5570,67 @@ from emp]]>
</Resource>
<Resource name="plan">
<![CDATA[
-LogicalProject(EXPR$0=[JSON_QUERY(JSON_API_COMMON_SYNTAX(JSON_VALUE_EXPRESSION($1),
'lax $'), FLAG(WITHOUT_ARRAY), FLAG(SqlJsonQueryEmptyOrErrorBehavior[NULL]),
FLAG(SqlJsonQueryEmptyOrErrorBehavior[NULL]))])
+LogicalProject(EXPR$0=[JSON_QUERY(JSON_API_COMMON_SYNTAX(FORMAT JSON($1), 'lax
$'), FLAG(WITHOUT_ARRAY), FLAG(SqlJsonQueryEmptyOrErrorBehavior[NULL]),
FLAG(SqlJsonQueryEmptyOrErrorBehavior[NULL]))])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testJsonType">
+ <Resource name="sql">
+ <![CDATA[select json_type(ename)
+from emp]]>
+ </Resource>
+ <Resource name="plan">
+ <![CDATA[
+LogicalProject(EXPR$0=[JSON_TYPE(FORMAT JSON($1))])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testJsonPretty">
+ <Resource name="sql">
+ <![CDATA[select json_pretty(ename)
+from emp]]>
+ </Resource>
+ <Resource name="plan">
+ <![CDATA[
+LogicalProject(EXPR$0=[JSON_PRETTY(FORMAT JSON($1))])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testJsonDepth">
+ <Resource name="sql">
+ <![CDATA[select json_depth(ename)
+from emp]]>
+ </Resource>
+ <Resource name="plan">
+ <![CDATA[
+LogicalProject(EXPR$0=[JSON_DEPTH(FORMAT JSON($1))])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testJsonLength">
+ <Resource name="sql">
+ <![CDATA[select json_length(ename, 'strict $')
+from emp]]>
+ </Resource>
+ <Resource name="plan">
+ <![CDATA[
+LogicalProject(EXPR$0=[JSON_LENGTH(FORMAT JSON($1), 'strict $')])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+]]>
+ </Resource>
+ </TestCase>
+ <TestCase name="testJsonKeys">
+ <Resource name="sql">
+ <![CDATA[select json_keys(ename, 'strict $')
+from emp]]>
+ </Resource>
+ <Resource name="plan">
+ <![CDATA[
+LogicalProject(EXPR$0=[JSON_KEYS(FORMAT JSON($1), 'strict $')])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
@@ -5567,7 +5642,7 @@ from emp]]>
</Resource>
<Resource name="plan">
<![CDATA[
-LogicalProject(EXPR$0=[JSON_ARRAY(FLAG(ABSENT_ON_NULL),
JSON_STRUCTURED_VALUE_EXPRESSION($1), JSON_STRUCTURED_VALUE_EXPRESSION($1))])
+LogicalProject(EXPR$0=[JSON_ARRAY(FLAG(ABSENT_ON_NULL), $1, $1)])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
@@ -5580,7 +5655,7 @@ from emp]]>
<Resource name="plan">
<![CDATA[
LogicalAggregate(group=[{}], EXPR$0=[JSON_ARRAYAGG_ABSENT_ON_NULL($0)])
- LogicalProject($f0=[JSON_STRUCTURED_VALUE_EXPRESSION($1)])
+ LogicalProject(ENAME=[$1])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
@@ -5592,8 +5667,8 @@ from emp]]>
</Resource>
<Resource name="plan">
<![CDATA[
-LogicalAggregate(group=[{}], EXPR$0=[JSON_ARRAYAGG_ABSENT_ON_NULL($0) WITHIN
GROUP ([1])])
- LogicalProject($f0=[JSON_STRUCTURED_VALUE_EXPRESSION($1)], ENAME=[$1])
+LogicalAggregate(group=[{}], EXPR$0=[JSON_ARRAYAGG_ABSENT_ON_NULL($0) WITHIN
GROUP ([0])])
+ LogicalProject(ENAME=[$1])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
@@ -5605,8 +5680,8 @@ from emp]]>
</Resource>
<Resource name="plan">
<![CDATA[
-LogicalAggregate(group=[{}], EXPR$0=[JSON_ARRAYAGG_NULL_ON_NULL($0) WITHIN
GROUP ([1])])
- LogicalProject($f0=[JSON_STRUCTURED_VALUE_EXPRESSION($1)], ENAME=[$1])
+LogicalAggregate(group=[{}], EXPR$0=[JSON_ARRAYAGG_NULL_ON_NULL($0) WITHIN
GROUP ([0])])
+ LogicalProject(ENAME=[$1])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
@@ -5618,8 +5693,8 @@ from emp]]>
</Resource>
<Resource name="plan">
<![CDATA[
-LogicalAggregate(group=[{}], EXPR$0=[JSON_ARRAYAGG_NULL_ON_NULL($0) WITHIN
GROUP ([1])])
- LogicalProject($f0=[JSON_STRUCTURED_VALUE_EXPRESSION($1)], ENAME=[$1])
+LogicalAggregate(group=[{}], EXPR$0=[JSON_ARRAYAGG_NULL_ON_NULL($0) WITHIN
GROUP ([0])])
+ LogicalProject(ENAME=[$1])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
@@ -5631,7 +5706,7 @@ from emp]]>
</Resource>
<Resource name="plan">
<![CDATA[
-LogicalProject(EXPR$0=[JSON_OBJECT(FLAG(NULL_ON_NULL), $1,
JSON_STRUCTURED_VALUE_EXPRESSION($7), $1,
JSON_STRUCTURED_VALUE_EXPRESSION($7))])
+LogicalProject(EXPR$0=[JSON_OBJECT(FLAG(NULL_ON_NULL), $1, $7, $1, $7)])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
@@ -5644,7 +5719,7 @@ from emp]]>
<Resource name="plan">
<![CDATA[
LogicalAggregate(group=[{}], EXPR$0=[JSON_OBJECTAGG_NULL_ON_NULL($0, $1)])
- LogicalProject(ENAME=[$1], $f1=[JSON_STRUCTURED_VALUE_EXPRESSION($7)])
+ LogicalProject(ENAME=[$1], DEPTNO=[$7])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
diff --git a/core/src/test/resources/sql/agg.iq
b/core/src/test/resources/sql/agg.iq
index c3fda24..4dba368 100644
--- a/core/src/test/resources/sql/agg.iq
+++ b/core/src/test/resources/sql/agg.iq
@@ -2602,27 +2602,26 @@ from emp group by gender;
(2 rows)
!ok
-EnumerableAggregate(group=[{0}], EXPR$1=[JSON_ARRAYAGG_ABSENT_ON_NULL($1)
WITHIN GROUP ([2])], EXPR$2=[JSON_ARRAYAGG_ABSENT_ON_NULL($1) WITHIN GROUP ([2
DESC])])
- EnumerableCalc(expr#0..1=[{inputs}],
expr#2=[JSON_STRUCTURED_VALUE_EXPRESSION($t0)], GENDER=[$t1], $f1=[$t2],
DEPTNO=[$t0])
- EnumerableUnion(all=[true])
- EnumerableCalc(expr#0=[{inputs}], expr#1=[10], expr#2=['F'],
EXPR$1=[$t1], EXPR$2=[$t2])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=[10], expr#2=['M'],
EXPR$1=[$t1], EXPR$2=[$t2])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=[20], expr#2=['M'],
EXPR$1=[$t1], EXPR$2=[$t2])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=[30], expr#2=['F'],
EXPR$1=[$t1], EXPR$2=[$t2])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=[30], expr#2=['F'],
EXPR$1=[$t1], EXPR$2=[$t2])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=[50], expr#2=['M'],
EXPR$1=[$t1], EXPR$2=[$t2])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=[50], expr#2=['F'],
EXPR$1=[$t1], EXPR$2=[$t2])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=[60], expr#2=['F'],
EXPR$1=[$t1], EXPR$2=[$t2])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=[null:INTEGER], expr#2=['F'],
EXPR$1=[$t1], EXPR$2=[$t2])
- EnumerableValues(tuples=[[{ 0 }]])
+EnumerableAggregate(group=[{1}], EXPR$1=[JSON_ARRAYAGG_ABSENT_ON_NULL($0)
WITHIN GROUP ([0])], EXPR$2=[JSON_ARRAYAGG_ABSENT_ON_NULL($0) WITHIN GROUP ([0
DESC])])
+ EnumerableUnion(all=[true])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=[10], expr#2=['F'], EXPR$1=[$t1],
EXPR$2=[$t2])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=[10], expr#2=['M'], EXPR$1=[$t1],
EXPR$2=[$t2])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=[20], expr#2=['M'], EXPR$1=[$t1],
EXPR$2=[$t2])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=[30], expr#2=['F'], EXPR$1=[$t1],
EXPR$2=[$t2])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=[30], expr#2=['F'], EXPR$1=[$t1],
EXPR$2=[$t2])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=[50], expr#2=['M'], EXPR$1=[$t1],
EXPR$2=[$t2])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=[50], expr#2=['F'], EXPR$1=[$t1],
EXPR$2=[$t2])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=[60], expr#2=['F'], EXPR$1=[$t1],
EXPR$2=[$t2])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=[null:INTEGER], expr#2=['F'],
EXPR$1=[$t1], EXPR$2=[$t2])
+ EnumerableValues(tuples=[[{ 0 }]])
!plan
# [CALCITE-2787] Json aggregate calls with different null clause get
incorrectly merged
@@ -2640,27 +2639,26 @@ from emp group by gender;
(2 rows)
!ok
-EnumerableAggregate(group=[{0}], EXPR$1=[JSON_ARRAYAGG_ABSENT_ON_NULL($1)],
EXPR$2=[JSON_ARRAYAGG_NULL_ON_NULL($1)])
- EnumerableCalc(expr#0..1=[{inputs}],
expr#2=[JSON_STRUCTURED_VALUE_EXPRESSION($t0)], GENDER=[$t1], $f1=[$t2])
- EnumerableUnion(all=[true])
- EnumerableCalc(expr#0=[{inputs}], expr#1=[10], expr#2=['F'],
EXPR$1=[$t1], EXPR$2=[$t2])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=[10], expr#2=['M'],
EXPR$1=[$t1], EXPR$2=[$t2])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=[20], expr#2=['M'],
EXPR$1=[$t1], EXPR$2=[$t2])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=[30], expr#2=['F'],
EXPR$1=[$t1], EXPR$2=[$t2])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=[30], expr#2=['F'],
EXPR$1=[$t1], EXPR$2=[$t2])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=[50], expr#2=['M'],
EXPR$1=[$t1], EXPR$2=[$t2])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=[50], expr#2=['F'],
EXPR$1=[$t1], EXPR$2=[$t2])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=[60], expr#2=['F'],
EXPR$1=[$t1], EXPR$2=[$t2])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=[null:INTEGER], expr#2=['F'],
EXPR$1=[$t1], EXPR$2=[$t2])
- EnumerableValues(tuples=[[{ 0 }]])
+EnumerableAggregate(group=[{1}], EXPR$1=[JSON_ARRAYAGG_ABSENT_ON_NULL($0)],
EXPR$2=[JSON_ARRAYAGG_NULL_ON_NULL($0)])
+ EnumerableUnion(all=[true])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=[10], expr#2=['F'], EXPR$1=[$t1],
EXPR$2=[$t2])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=[10], expr#2=['M'], EXPR$1=[$t1],
EXPR$2=[$t2])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=[20], expr#2=['M'], EXPR$1=[$t1],
EXPR$2=[$t2])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=[30], expr#2=['F'], EXPR$1=[$t1],
EXPR$2=[$t2])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=[30], expr#2=['F'], EXPR$1=[$t1],
EXPR$2=[$t2])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=[50], expr#2=['M'], EXPR$1=[$t1],
EXPR$2=[$t2])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=[50], expr#2=['F'], EXPR$1=[$t1],
EXPR$2=[$t2])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=[60], expr#2=['F'], EXPR$1=[$t1],
EXPR$2=[$t2])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=[null:INTEGER], expr#2=['F'],
EXPR$1=[$t1], EXPR$2=[$t2])
+ EnumerableValues(tuples=[[{ 0 }]])
!plan
select gender,
@@ -2676,27 +2674,26 @@ from emp group by gender;
(2 rows)
!ok
-EnumerableAggregate(group=[{0}], EXPR$1=[JSON_OBJECTAGG_NULL_ON_NULL($1, $2)],
EXPR$2=[JSON_OBJECTAGG_ABSENT_ON_NULL($1, $2)])
- EnumerableCalc(expr#0..2=[{inputs}],
expr#3=[JSON_STRUCTURED_VALUE_EXPRESSION($t1)], GENDER=[$t2], ENAME=[$t0],
$f2=[$t3])
- EnumerableUnion(all=[true])
- EnumerableCalc(expr#0=[{inputs}], expr#1=['Jane'], expr#2=[10],
expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=['Bob'], expr#2=[10],
expr#3=['M'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=['Eric'], expr#2=[20],
expr#3=['M'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=['Susan'], expr#2=[30],
expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=['Alice'], expr#2=[30],
expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=['Adam'], expr#2=[50],
expr#3=['M'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=['Eve'], expr#2=[50],
expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=['Grace'], expr#2=[60],
expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
- EnumerableValues(tuples=[[{ 0 }]])
- EnumerableCalc(expr#0=[{inputs}], expr#1=['Wilma'],
expr#2=[null:INTEGER], expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
- EnumerableValues(tuples=[[{ 0 }]])
+EnumerableAggregate(group=[{2}], EXPR$1=[JSON_OBJECTAGG_NULL_ON_NULL($0, $1)],
EXPR$2=[JSON_OBJECTAGG_ABSENT_ON_NULL($0, $1)])
+ EnumerableUnion(all=[true])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=['Jane'], expr#2=[10],
expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=['Bob'], expr#2=[10],
expr#3=['M'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=['Eric'], expr#2=[20],
expr#3=['M'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=['Susan'], expr#2=[30],
expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=['Alice'], expr#2=[30],
expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=['Adam'], expr#2=[50],
expr#3=['M'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=['Eve'], expr#2=[50],
expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=['Grace'], expr#2=[60],
expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
+ EnumerableValues(tuples=[[{ 0 }]])
+ EnumerableCalc(expr#0=[{inputs}], expr#1=['Wilma'], expr#2=[null:INTEGER],
expr#3=['F'], EXPR$0=[$t1], EXPR$1=[$t2], EXPR$2=[$t3])
+ EnumerableValues(tuples=[[{ 0 }]])
!plan
select listagg(ename) as combined_name from emp;